Merge pull request #56 from Smithay/feature/udev
Udev backend and session api
This commit is contained in:
commit
fa6837e86d
|
@ -2,3 +2,4 @@ target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
*.bk
|
*.bk
|
||||||
.vscode
|
.vscode
|
||||||
|
.vagga
|
||||||
|
|
73
.travis.yml
73
.travis.yml
|
@ -1,46 +1,41 @@
|
||||||
language: rust
|
language: rust
|
||||||
|
|
||||||
cache: cargo
|
# We need this for the matrix, install is quick although unused
|
||||||
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
os:
|
sudo: required
|
||||||
- linux
|
|
||||||
|
|
||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
|
# We cannot cache .vagga, because we actually do not have read permissions
|
||||||
|
# without sudo and travis cache script runs not as sudo...
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- .vagga/stable-home
|
||||||
|
- .vagga/beta-home
|
||||||
|
- .vagga/nightly-home
|
||||||
|
- .vagga/.cache
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- libssl-dev
|
- libcurl4-openssl-dev
|
||||||
- libudev-dev
|
- libelf-dev
|
||||||
- libgbm-dev
|
- libdw-dev
|
||||||
- libxkbcommon-dev
|
|
||||||
- libegl1-mesa-dev
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sudo add-apt-repository -y ppa:wayland.admin/daily-builds
|
|
||||||
- sudo apt-get update -qq || echo "Ignoring failed apt-get update..."
|
|
||||||
- sudo apt-get install -y libwayland-dev libinput-dev
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
|
||||||
- RUST_BACKTRACE=1
|
|
||||||
matrix:
|
matrix:
|
||||||
- FEATURES=""
|
- FEATURES=""
|
||||||
# test individual features
|
# test individual features
|
||||||
- FEATURES="backend_winit"
|
- FEATURES="backend_winit"
|
||||||
- FEATURES="backend_drm"
|
- FEATURES="backend_drm"
|
||||||
- FEATURES="backend_libinput"
|
- FEATURES="backend_libinput"
|
||||||
|
- FEATURES="backend_udev"
|
||||||
|
- FEATURES="backend_session"
|
||||||
|
- FEATURES="backend_session_udev"
|
||||||
- FEATURES="renderer_glium"
|
- FEATURES="renderer_glium"
|
||||||
# test default features
|
# test default features
|
||||||
- FEATURES="default"
|
- FEATURES="default"
|
||||||
|
@ -58,38 +53,36 @@ matrix:
|
||||||
- rust: nightly
|
- rust: nightly
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
- 'echo ubuntu-mirror: http://mirrors.us.kernel.org/ubuntu/ > ~/.vagga.yaml'
|
||||||
|
- 'echo alpine-mirror: http://mirrors.gigenet.com/alpinelinux/ >> ~/.vagga.yaml'
|
||||||
- |
|
- |
|
||||||
if [ "$FEATURES" = "cargo-fmt" ]; then
|
echo "$(id -un):100000:65536" | sudo tee /etc/subuid | sudo tee /etc/subgid
|
||||||
cargo install rustfmt-nightly --force;
|
sudo apt-get install uidmap -y
|
||||||
fi
|
curl http://files.zerogw.com/vagga/vagga-install.sh | sh
|
||||||
- |
|
- vagga update-$TRAVIS_RUST_VERSION
|
||||||
if [ "$FEATURES" = "cargo-clippy" ]; then
|
|
||||||
cargo install clippy --force;
|
|
||||||
fi
|
|
||||||
- mkdir $(pwd)/socket
|
|
||||||
- export XDG_RUNTIME_DIR="$(pwd)/socket"
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
case $FEATURES in
|
case $FEATURES in
|
||||||
"all")
|
"all")
|
||||||
cargo test --all-features &&
|
vagga cargo-$TRAVIS_RUST_VERSION test --all-features &&
|
||||||
cargo doc --no-deps --all-features
|
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps --all-features
|
||||||
;;
|
;;
|
||||||
"default")
|
"default")
|
||||||
cargo test &&
|
vagga cargo-$TRAVIS_RUST_VERSION test &&
|
||||||
cargo doc --no-deps
|
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps
|
||||||
;;
|
;;
|
||||||
"cargo-fmt")
|
"cargo-fmt")
|
||||||
cargo fmt -- --write-mode=diff
|
vagga cargo-$TRAVIS_RUST_VERSION install -f rustfmt-nightly &&
|
||||||
|
vagga cargo-$TRAVIS_RUST_VERSION fmt -- --write-mode=diff
|
||||||
;;
|
;;
|
||||||
"cargo-clippy")
|
"cargo-clippy")
|
||||||
cargo clippy --all-features -- -D warnings
|
vagga cargo-$TRAVIS_RUST_VERSION install -f clippy &&
|
||||||
|
vagga cargo-$TRAVIS_RUST_VERSION clippy --all-features -- -D warnings
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
cargo test --lib --doc --tests --no-default-features --features "$FEATURES" &&
|
vagga cargo-$TRAVIS_RUST_VERSION test --lib --doc --tests --no-default-features --features "$FEATURES" &&
|
||||||
cargo doc --no-deps --no-default-features --features "$FEATURES"
|
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps --no-default-features --features "$FEATURES"
|
||||||
esac
|
esac
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -16,13 +16,14 @@ slog-stdlog = "2.0.0-0.2"
|
||||||
libloading = "0.4.0"
|
libloading = "0.4.0"
|
||||||
wayland-client = { version = "0.9.9", optional = true }
|
wayland-client = { version = "0.9.9", optional = true }
|
||||||
winit = { version = "0.8.2", optional = true }
|
winit = { version = "0.8.2", optional = true }
|
||||||
drm = { version = "^0.3.0", optional = true }
|
drm = { version = "^0.3.1", optional = true }
|
||||||
gbm = { version = "^0.2.2", optional = true }
|
gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] }
|
||||||
glium = { version = "0.17.1", optional = true, default-features = false }
|
glium = { version = "0.17.1", optional = true, default-features = false }
|
||||||
input = { version = "0.2.0", optional = true }
|
input = { version = "0.4.0", optional = true }
|
||||||
|
udev = { version = "0.2.0", optional = true }
|
||||||
rental = "0.4.11"
|
rental = "0.4.11"
|
||||||
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] }
|
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] }
|
||||||
image = "0.16.0"
|
image = "0.17.0"
|
||||||
error-chain = "0.11.0"
|
error-chain = "0.11.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -32,10 +33,14 @@ gl_generator = "0.5"
|
||||||
slog-term = "2.0"
|
slog-term = "2.0"
|
||||||
slog-async = "2.0"
|
slog-async = "2.0"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
|
ctrlc = { version = "3.0", features = ["termination"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backend_winit", "backend_drm", "backend_libinput", "renderer_glium"]
|
default = ["backend_winit", "backend_drm", "backend_libinput", "backend_udev", "renderer_glium"]
|
||||||
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"]
|
||||||
backend_drm = ["drm", "gbm"]
|
backend_drm = ["drm", "gbm"]
|
||||||
backend_libinput = ["input"]
|
backend_libinput = ["input"]
|
||||||
|
backend_session = []
|
||||||
|
backend_session_udev = ["udev", "backend_session"]
|
||||||
|
backend_udev = ["udev", "backend_drm", "backend_session_udev"]
|
||||||
renderer_glium = ["glium"]
|
renderer_glium = ["glium"]
|
||||||
|
|
|
@ -15,6 +15,7 @@ mod helpers;
|
||||||
|
|
||||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
use drm::control::crtc;
|
||||||
use drm::control::encoder::Info as EncoderInfo;
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
|
@ -30,7 +31,7 @@ use std::cell::RefCell;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{EventLoopHandle, StateToken};
|
use wayland_server::{StateToken, StateProxy};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// A logger facility, here we use the terminal for this example
|
// A logger facility, here we use the terminal for this example
|
||||||
|
@ -80,11 +81,20 @@ fn main() {
|
||||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||||
|
|
||||||
|
{
|
||||||
// Initialize the hardware backend
|
// Initialize the hardware backend
|
||||||
let renderer_token = device
|
let renderer = device
|
||||||
.create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()])
|
.create_backend(event_loop.state(), crtc, mode, vec![connector_info.handle()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize glium
|
||||||
|
*/
|
||||||
|
let mut frame = event_loop.state().get(renderer).draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
*/
|
*/
|
||||||
|
@ -93,16 +103,6 @@ fn main() {
|
||||||
|
|
||||||
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize glium
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
let drawer = event_loop.state().get(&renderer_token);
|
|
||||||
let mut frame = drawer.draw();
|
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
|
||||||
frame.finish().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a listening socket:
|
* Add a listening socket:
|
||||||
*/
|
*/
|
||||||
|
@ -112,9 +112,10 @@ fn main() {
|
||||||
/*
|
/*
|
||||||
* Register the DrmDevice on the EventLoop
|
* Register the DrmDevice on the EventLoop
|
||||||
*/
|
*/
|
||||||
|
let device_token = event_loop.state().insert(device);
|
||||||
let _source = drm_device_bind(
|
let _source = drm_device_bind(
|
||||||
&mut event_loop,
|
&mut event_loop,
|
||||||
device,
|
device_token,
|
||||||
DrmHandlerImpl {
|
DrmHandlerImpl {
|
||||||
shell_state_token,
|
shell_state_token,
|
||||||
compositor_token,
|
compositor_token,
|
||||||
|
@ -139,9 +140,9 @@ pub struct DrmHandlerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) {
|
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
||||||
let state = evlh.state();
|
let state = state.into();
|
||||||
let drawer = state.get(backend);
|
let drawer = state.get(backend);
|
||||||
let mut frame = drawer.draw();
|
let mut frame = drawer.draw();
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
@ -185,7 +186,7 @@ impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
frame.finish().unwrap();
|
frame.finish().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
error: DrmError) {
|
error: DrmError) {
|
||||||
panic!("{:?}", error);
|
panic!("{:?}", error);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,477 @@
|
||||||
|
extern crate drm;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate glium;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate input as libinput;
|
||||||
|
extern crate image;
|
||||||
|
extern crate udev;
|
||||||
|
#[macro_use(define_roles)]
|
||||||
|
extern crate smithay;
|
||||||
|
extern crate xkbcommon;
|
||||||
|
extern crate wayland_server;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate slog;
|
||||||
|
extern crate slog_async;
|
||||||
|
extern crate slog_term;
|
||||||
|
|
||||||
|
extern crate ctrlc;
|
||||||
|
|
||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
|
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
use drm::control::encoder::Info as EncoderInfo;
|
||||||
|
use drm::control::crtc;
|
||||||
|
use drm::result::Error as DrmError;
|
||||||
|
use glium::Surface;
|
||||||
|
use image::{ImageBuffer, Rgba};
|
||||||
|
use libinput::{Libinput, Device as LibinputDevice, event};
|
||||||
|
use libinput::event::keyboard::KeyboardEventTrait;
|
||||||
|
use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
||||||
|
use slog::{Drain, Logger};
|
||||||
|
use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler};
|
||||||
|
use smithay::backend::graphics::GraphicsBackend;
|
||||||
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent,
|
||||||
|
PointerAxisEvent, KeyState};
|
||||||
|
use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface};
|
||||||
|
use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind};
|
||||||
|
use smithay::backend::session::{Session, SessionNotifier};
|
||||||
|
use smithay::backend::session::direct::{direct_session_bind, DirectSession};
|
||||||
|
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||||
|
use smithay::wayland::compositor::roles::Role;
|
||||||
|
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
|
||||||
|
use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat};
|
||||||
|
use smithay::wayland::shell::ShellState;
|
||||||
|
use smithay::wayland::shm::init_shm_global;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::io::Error as IoError;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::process::Command;
|
||||||
|
use xkbcommon::xkb::keysyms as xkb;
|
||||||
|
use wayland_server::{StateToken, StateProxy};
|
||||||
|
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||||
|
|
||||||
|
struct LibinputInputHandler {
|
||||||
|
log: Logger,
|
||||||
|
pointer: PointerHandle,
|
||||||
|
keyboard: KeyboardHandle,
|
||||||
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
|
screen_size: (u32, u32),
|
||||||
|
serial: u32,
|
||||||
|
running: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LibinputInputHandler {
|
||||||
|
fn next_serial(&mut self) -> u32 {
|
||||||
|
self.serial += 1;
|
||||||
|
self.serial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputHandler<LibinputInputBackend> for LibinputInputHandler {
|
||||||
|
fn on_seat_created(&mut self, _: &input::Seat) {
|
||||||
|
/* we just create a single static one */
|
||||||
|
}
|
||||||
|
fn on_seat_destroyed(&mut self, _: &input::Seat) {
|
||||||
|
/* we just create a single static one */
|
||||||
|
}
|
||||||
|
fn on_seat_changed(&mut self, _: &input::Seat) {
|
||||||
|
/* we just create a single static one */
|
||||||
|
}
|
||||||
|
fn on_keyboard_key(&mut self, _: &input::Seat, evt: event::keyboard::KeyboardKeyEvent) {
|
||||||
|
let keycode = evt.key();
|
||||||
|
let state = evt.state();
|
||||||
|
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||||
|
|
||||||
|
let serial = self.next_serial();
|
||||||
|
self.keyboard.input(keycode, state, serial, |modifiers, keysym| {
|
||||||
|
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace {
|
||||||
|
self.running.store(false, Ordering::SeqCst);
|
||||||
|
false
|
||||||
|
} else if modifiers.logo && keysym == xkb::KEY_Return && state == KeyState::Pressed {
|
||||||
|
let _ = Command::new("weston-terminal").spawn();
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn on_pointer_move(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionEvent) {
|
||||||
|
let (x, y) = (evt.dx(), evt.dy());
|
||||||
|
let serial = self.next_serial();
|
||||||
|
let mut location = self.pointer_location.borrow_mut();
|
||||||
|
location.0 += x;
|
||||||
|
location.1 += y;
|
||||||
|
let under = self.window_map.borrow().get_surface_under((location.0, location.1));
|
||||||
|
self.pointer.motion(
|
||||||
|
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
|
||||||
|
serial,
|
||||||
|
evt.time(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: event::pointer::PointerMotionAbsoluteEvent) {
|
||||||
|
let (x, y) = (evt.absolute_x_transformed(self.screen_size.0), evt.absolute_y_transformed(self.screen_size.1));
|
||||||
|
*self.pointer_location.borrow_mut() = (x, y);
|
||||||
|
let serial = self.next_serial();
|
||||||
|
let under = self.window_map.borrow().get_surface_under((x, y));
|
||||||
|
self.pointer.motion(
|
||||||
|
under.as_ref().map(|&(ref s, (x, y))| (s, x, y)),
|
||||||
|
serial,
|
||||||
|
evt.time(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn on_pointer_button(&mut self, _: &input::Seat, evt: event::pointer::PointerButtonEvent) {
|
||||||
|
let serial = self.next_serial();
|
||||||
|
let button = evt.button();
|
||||||
|
let state = match evt.state() {
|
||||||
|
input::MouseButtonState::Pressed => {
|
||||||
|
// change the keyboard focus
|
||||||
|
let under = self.window_map
|
||||||
|
.borrow_mut()
|
||||||
|
.get_surface_and_bring_to_top(*self.pointer_location.borrow());
|
||||||
|
self.keyboard
|
||||||
|
.set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
|
||||||
|
wl_pointer::ButtonState::Pressed
|
||||||
|
}
|
||||||
|
input::MouseButtonState::Released => wl_pointer::ButtonState::Released,
|
||||||
|
};
|
||||||
|
self.pointer.button(button, state, serial, evt.time());
|
||||||
|
}
|
||||||
|
fn on_pointer_axis(&mut self, _: &input::Seat, evt: LibinputPointerAxisEvent) {
|
||||||
|
let axis = match evt.axis() {
|
||||||
|
input::Axis::Vertical => wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||||
|
input::Axis::Horizontal => wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||||
|
};
|
||||||
|
self.pointer.axis(axis, evt.amount(), evt.time());
|
||||||
|
}
|
||||||
|
fn on_touch_down(&mut self, _: &input::Seat, _: event::touch::TouchDownEvent) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
fn on_touch_motion(&mut self, _: &input::Seat, _: event::touch::TouchMotionEvent) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
fn on_touch_up(&mut self, _: &input::Seat, _: event::touch::TouchUpEvent) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
fn on_touch_cancel(&mut self, _: &input::Seat, _: event::touch::TouchCancelEvent) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
fn on_touch_frame(&mut self, _: &input::Seat, _: event::touch::TouchFrameEvent) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
fn on_input_config_changed(&mut self, _: &mut [LibinputDevice]) {
|
||||||
|
/* not done in this example */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// A logger facility, here we use the terminal for this example
|
||||||
|
let log = Logger::root(
|
||||||
|
slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(),
|
||||||
|
o!(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize the wayland server
|
||||||
|
let (mut display, mut event_loop) = wayland_server::create_display();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the compositor
|
||||||
|
*/
|
||||||
|
init_shm_global(&mut event_loop, vec![], log.clone());
|
||||||
|
|
||||||
|
let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize session on the current tty
|
||||||
|
*/
|
||||||
|
let (session, mut notifier) = DirectSession::new(None, log.clone()).unwrap();
|
||||||
|
let session = Rc::new(RefCell::new(session));
|
||||||
|
|
||||||
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
let r = running.clone();
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
r.store(false, Ordering::SeqCst);
|
||||||
|
}).expect("Error setting Ctrl-C handler");
|
||||||
|
|
||||||
|
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the udev backend
|
||||||
|
*/
|
||||||
|
let context = udev::Context::new().unwrap();
|
||||||
|
let bytes = include_bytes!("resources/cursor2.rgba");
|
||||||
|
let udev_token
|
||||||
|
= UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl {
|
||||||
|
shell_state_token,
|
||||||
|
compositor_token,
|
||||||
|
window_map: window_map.clone(),
|
||||||
|
pointer_location: pointer_location.clone(),
|
||||||
|
pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(),
|
||||||
|
logger: log.clone(),
|
||||||
|
}, log.clone()).unwrap();
|
||||||
|
|
||||||
|
let udev_session_id = notifier.register(udev_token.clone());
|
||||||
|
|
||||||
|
let (seat_token, _) = Seat::new(&mut event_loop, session.seat().into(), log.clone());
|
||||||
|
|
||||||
|
let pointer = event_loop.state().get_mut(&seat_token).add_pointer();
|
||||||
|
let keyboard = event_loop
|
||||||
|
.state()
|
||||||
|
.get_mut(&seat_token)
|
||||||
|
.add_keyboard("", "", "", None, 1000, 500)
|
||||||
|
.expect("Failed to initialize the keyboard");
|
||||||
|
|
||||||
|
let (output_token, _output_global) = Output::new(
|
||||||
|
&mut event_loop,
|
||||||
|
"Drm".into(),
|
||||||
|
PhysicalProperties {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
subpixel: wl_output::Subpixel::Unknown,
|
||||||
|
maker: "Smithay".into(),
|
||||||
|
model: "Generic DRM".into(),
|
||||||
|
},
|
||||||
|
log.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (w, h) = (1920, 1080); // Hardcode full-hd res
|
||||||
|
event_loop
|
||||||
|
.state()
|
||||||
|
.get_mut(&output_token)
|
||||||
|
.change_current_state(
|
||||||
|
Some(Mode {
|
||||||
|
width: w as i32,
|
||||||
|
height: h as i32,
|
||||||
|
refresh: 60_000,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
event_loop
|
||||||
|
.state()
|
||||||
|
.get_mut(&output_token)
|
||||||
|
.set_preferred(Mode {
|
||||||
|
width: w as i32,
|
||||||
|
height: h as i32,
|
||||||
|
refresh: 60_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize libinput backend
|
||||||
|
*/
|
||||||
|
let seat = session.seat();
|
||||||
|
let mut libinput_context = Libinput::new_from_udev::<LibinputSessionInterface<Rc<RefCell<DirectSession>>>>(session.into(), &context);
|
||||||
|
let libinput_session_id = notifier.register(libinput_context.clone());
|
||||||
|
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||||
|
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
||||||
|
libinput_backend.set_handler(LibinputInputHandler {
|
||||||
|
log: log.clone(),
|
||||||
|
pointer,
|
||||||
|
keyboard,
|
||||||
|
window_map: window_map.clone(),
|
||||||
|
pointer_location,
|
||||||
|
screen_size: (w, h),
|
||||||
|
serial: 0,
|
||||||
|
running: running.clone(),
|
||||||
|
});
|
||||||
|
let libinput_event_source = libinput_bind(libinput_backend, &mut event_loop).unwrap();
|
||||||
|
|
||||||
|
let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap();
|
||||||
|
let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a listening socket
|
||||||
|
*/
|
||||||
|
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||||
|
println!("Listening on socket: {}", name);
|
||||||
|
|
||||||
|
while running.load(Ordering::SeqCst) {
|
||||||
|
event_loop.dispatch(Some(16));
|
||||||
|
display.flush_clients();
|
||||||
|
window_map.borrow_mut().refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Bye Bye");
|
||||||
|
|
||||||
|
let mut notifier = session_event_source.remove();
|
||||||
|
notifier.unregister(udev_session_id);
|
||||||
|
notifier.unregister(libinput_session_id);
|
||||||
|
|
||||||
|
libinput_event_source.remove();
|
||||||
|
|
||||||
|
let udev_token = udev_event_source.remove();
|
||||||
|
let udev = event_loop.state().remove(udev_token);
|
||||||
|
udev.close(event_loop.state());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UdevHandlerImpl {
|
||||||
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
||||||
|
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
||||||
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
|
pointer_image: ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UdevHandlerImpl {
|
||||||
|
pub fn scan_connectors<'a, S: Into<StateProxy<'a>>>(&self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) {
|
||||||
|
// Get a set of all modesetting resource handles (excluding planes):
|
||||||
|
let res_handles = device.resource_handles().unwrap();
|
||||||
|
|
||||||
|
// Use first connected connector
|
||||||
|
let connector_infos: Vec<ConnectorInfo> = res_handles
|
||||||
|
.connectors()
|
||||||
|
.iter()
|
||||||
|
.map(|conn| {
|
||||||
|
ConnectorInfo::load_from_device(device, *conn).unwrap()
|
||||||
|
})
|
||||||
|
.filter(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||||
|
.inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut used_crtcs: HashSet<crtc::Handle> = HashSet::new();
|
||||||
|
|
||||||
|
let mut state = state.into();
|
||||||
|
|
||||||
|
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
|
||||||
|
for connector_info in connector_infos {
|
||||||
|
let encoder_infos = connector_info.encoders().iter().flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle)).collect::<Vec<EncoderInfo>>();
|
||||||
|
for encoder_info in encoder_infos {
|
||||||
|
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||||
|
if !used_crtcs.contains(&crtc) {
|
||||||
|
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||||
|
// create a backend
|
||||||
|
let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap();
|
||||||
|
|
||||||
|
// create cursor
|
||||||
|
{
|
||||||
|
let renderer = state.get_mut(renderer_token);
|
||||||
|
renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render first frame
|
||||||
|
{
|
||||||
|
let renderer = state.get_mut(renderer_token);
|
||||||
|
let mut frame = renderer.draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
frame.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
used_crtcs.insert(crtc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UdevHandler<GliumDrawer<DrmBackend>, DrmHandlerImpl> for UdevHandlerImpl {
|
||||||
|
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<GliumDrawer<DrmBackend>>) -> Option<DrmHandlerImpl>
|
||||||
|
{
|
||||||
|
self.scan_connectors(state, device);
|
||||||
|
|
||||||
|
Some(DrmHandlerImpl {
|
||||||
|
shell_state_token: self.shell_state_token.clone(),
|
||||||
|
compositor_token: self.compositor_token.clone(),
|
||||||
|
window_map: self.window_map.clone(),
|
||||||
|
pointer_location: self.pointer_location.clone(),
|
||||||
|
logger: self.logger.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
||||||
|
//quick and dirt, just re-init the device
|
||||||
|
let mut state = state.into();
|
||||||
|
self.device_removed(&mut state, device);
|
||||||
|
state.with_value(device, |state, device| self.scan_connectors(state, device));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<GliumDrawer<DrmBackend>>>) {
|
||||||
|
state.into().with_value(device, |state, device| {
|
||||||
|
let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::<Vec<crtc::Handle>>();
|
||||||
|
let mut state: StateProxy = state.into();
|
||||||
|
for crtc in crtcs {
|
||||||
|
device.destroy_backend(&mut state, &crtc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, error: IoError) {
|
||||||
|
error!(self.logger, "{:?}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DrmHandlerImpl {
|
||||||
|
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
|
||||||
|
compositor_token: CompositorToken<SurfaceData, Roles, ()>,
|
||||||
|
window_map: Rc<RefCell<MyWindowMap>>,
|
||||||
|
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
|
||||||
|
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
|
backend: &StateToken<GliumDrawer<DrmBackend>>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) {
|
||||||
|
let state = state.into();
|
||||||
|
let drawer = state.get(backend);
|
||||||
|
{
|
||||||
|
let (x, y) = *self.pointer_location.borrow();
|
||||||
|
let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
||||||
|
}
|
||||||
|
let mut frame = drawer.draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
// redraw the frame, in a simple but inneficient way
|
||||||
|
{
|
||||||
|
let screen_dimensions = drawer.get_framebuffer_dimensions();
|
||||||
|
self.window_map
|
||||||
|
.borrow()
|
||||||
|
.with_windows_from_bottom_to_top(|toplevel_surface, initial_place| {
|
||||||
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
|
self.compositor_token
|
||||||
|
.with_surface_tree_upward(
|
||||||
|
wl_surface,
|
||||||
|
initial_place,
|
||||||
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|
if let Some((ref contents, (w, h))) = attributes.user_data.buffer {
|
||||||
|
// there is actually something to draw !
|
||||||
|
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||||
|
x += subdata.x;
|
||||||
|
y += subdata.y;
|
||||||
|
}
|
||||||
|
drawer.render(
|
||||||
|
&mut frame,
|
||||||
|
contents,
|
||||||
|
(w, h),
|
||||||
|
(x, y),
|
||||||
|
screen_dimensions,
|
||||||
|
);
|
||||||
|
TraversalAction::DoChildren((x, y))
|
||||||
|
} else {
|
||||||
|
// we are not display, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Err(err) = frame.finish() {
|
||||||
|
error!(self.logger, "Error during rendering: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, _state: S, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
|
||||||
|
error: DrmError) {
|
||||||
|
error!(self.logger, "{:?}", error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,6 @@ rental! {
|
||||||
}
|
}
|
||||||
use self::graphics::{Graphics, Surface};
|
use self::graphics::{Graphics, Surface};
|
||||||
|
|
||||||
|
|
||||||
/// Backend based on a `DrmDevice` and a given crtc
|
/// Backend based on a `DrmDevice` and a given crtc
|
||||||
pub struct DrmBackend {
|
pub struct DrmBackend {
|
||||||
graphics: Graphics,
|
graphics: Graphics,
|
||||||
|
@ -71,9 +70,10 @@ pub struct DrmBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrmBackend {
|
impl DrmBackend {
|
||||||
pub(crate) fn new(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode,
|
pub(crate) fn new(
|
||||||
connectors: Vec<connector::Handle>, logger: ::slog::Logger)
|
context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: Vec<connector::Handle>,
|
||||||
-> Result<DrmBackend> {
|
logger: ::slog::Logger,
|
||||||
|
) -> Result<DrmBackend> {
|
||||||
// logger already initialized by the DrmDevice
|
// logger already initialized by the DrmDevice
|
||||||
let log = ::slog_or_stdlog(logger);
|
let log = ::slog_or_stdlog(logger);
|
||||||
info!(log, "Initializing DrmBackend");
|
info!(log, "Initializing DrmBackend");
|
||||||
|
@ -92,7 +92,7 @@ impl DrmBackend {
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
GbmFormat::ARGB8888,
|
GbmFormat::ARGB8888,
|
||||||
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
|
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||||
)
|
)
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
},
|
},
|
||||||
|
@ -107,7 +107,7 @@ impl DrmBackend {
|
||||||
w as u32,
|
w as u32,
|
||||||
h as u32,
|
h as u32,
|
||||||
GbmFormat::XRGB8888,
|
GbmFormat::XRGB8888,
|
||||||
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
|
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||||
)
|
)
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
},
|
},
|
||||||
|
@ -145,9 +145,7 @@ impl DrmBackend {
|
||||||
&connectors,
|
&connectors,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
Some(mode),
|
Some(mode),
|
||||||
).chain_err(
|
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?;
|
||||||
|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)),
|
|
||||||
)?;
|
|
||||||
front_bo.set_userdata(fb);
|
front_bo.set_userdata(fb);
|
||||||
|
|
||||||
Ok(EGL {
|
Ok(EGL {
|
||||||
|
@ -176,11 +174,9 @@ impl DrmBackend {
|
||||||
let next_bo = egl.buffers.next_buffer.replace(None);
|
let next_bo = egl.buffers.next_buffer.replace(None);
|
||||||
|
|
||||||
if let Some(next_buffer) = next_bo {
|
if let Some(next_buffer) = next_bo {
|
||||||
trace!(self.logger, "Releasing all front buffer");
|
trace!(self.logger, "Releasing old front buffer");
|
||||||
egl.buffers.front_buffer.set(next_buffer);
|
egl.buffers.front_buffer.set(next_buffer);
|
||||||
// drop and release the old buffer
|
// drop and release the old buffer
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -193,9 +189,7 @@ impl DrmBackend {
|
||||||
/// Errors if the new connector does not support the currently set `Mode`
|
/// Errors if the new connector does not support the currently set `Mode`
|
||||||
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
|
||||||
let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
|
let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
|
||||||
.chain_err(|| {
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?;
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// check if the connector can handle the current mode
|
// check if the connector can handle the current mode
|
||||||
if info.modes().contains(&self.mode) {
|
if info.modes().contains(&self.mode) {
|
||||||
|
@ -203,9 +197,8 @@ impl DrmBackend {
|
||||||
let encoders = info.encoders()
|
let encoders = info.encoders()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| {
|
.map(|encoder| {
|
||||||
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder).chain_err(
|
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder)
|
||||||
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<encoder::Info>>>()?;
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
|
@ -215,9 +208,7 @@ impl DrmBackend {
|
||||||
.head()
|
.head()
|
||||||
.head()
|
.head()
|
||||||
.resource_handles()
|
.resource_handles()
|
||||||
.chain_err(|| {
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?;
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
|
||||||
})?;
|
|
||||||
if !encoders
|
if !encoders
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| encoder.possible_crtcs())
|
.map(|encoder| encoder.possible_crtcs())
|
||||||
|
@ -272,9 +263,7 @@ impl DrmBackend {
|
||||||
// check the connectors
|
// check the connectors
|
||||||
for connector in &self.connectors {
|
for connector in &self.connectors {
|
||||||
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
|
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
|
||||||
.chain_err(|| {
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
|
||||||
})?
|
|
||||||
.modes()
|
.modes()
|
||||||
.contains(&mode)
|
.contains(&mode)
|
||||||
{
|
{
|
||||||
|
@ -295,9 +284,7 @@ impl DrmBackend {
|
||||||
// resolution.
|
// resolution.
|
||||||
debug!(
|
debug!(
|
||||||
logger_ref,
|
logger_ref,
|
||||||
"Reinitializing surface for new mode: {}:{}",
|
"Reinitializing surface for new mode: {}:{}", w, h
|
||||||
w,
|
|
||||||
h
|
|
||||||
);
|
);
|
||||||
graphics.gbm.surface = Surface::try_new(
|
graphics.gbm.surface = Surface::try_new(
|
||||||
{
|
{
|
||||||
|
@ -311,7 +298,7 @@ impl DrmBackend {
|
||||||
w as u32,
|
w as u32,
|
||||||
h as u32,
|
h as u32,
|
||||||
GbmFormat::XRGB8888,
|
GbmFormat::XRGB8888,
|
||||||
&[BufferObjectFlags::Scanout, BufferObjectFlags::Rendering],
|
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
|
||||||
)
|
)
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
.chain_err(|| ErrorKind::GbmInitFailed)?)
|
||||||
},
|
},
|
||||||
|
@ -340,9 +327,8 @@ impl DrmBackend {
|
||||||
front_bo.format()
|
front_bo.format()
|
||||||
);
|
);
|
||||||
// we need a framebuffer per front_buffer
|
// we need a framebuffer per front_buffer
|
||||||
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| {
|
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo)
|
||||||
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
|
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
debug!(logger_ref, "Initialize screen");
|
debug!(logger_ref, "Initialize screen");
|
||||||
crtc::set(
|
crtc::set(
|
||||||
|
@ -352,9 +338,7 @@ impl DrmBackend {
|
||||||
connectors_ref,
|
connectors_ref,
|
||||||
(0, 0),
|
(0, 0),
|
||||||
Some(mode),
|
Some(mode),
|
||||||
).chain_err(|| {
|
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?;
|
||||||
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
|
|
||||||
})?;
|
|
||||||
front_bo.set_userdata(fb);
|
front_bo.set_userdata(fb);
|
||||||
|
|
||||||
Ok(EGL {
|
Ok(EGL {
|
||||||
|
@ -374,12 +358,18 @@ impl DrmBackend {
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the crtc id used by this backend
|
||||||
|
pub fn crtc(&self) -> crtc::Handle {
|
||||||
|
self.crtc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for DrmBackend {
|
impl Drop for DrmBackend {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
// Drop framebuffers attached to the userdata of the gbm surface buffers.
|
||||||
// (They don't implement drop, as they need the device)
|
// (They don't implement drop, as they need the device)
|
||||||
|
let crtc = self.crtc;
|
||||||
self.graphics.rent_all_mut(|graphics| {
|
self.graphics.rent_all_mut(|graphics| {
|
||||||
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
|
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
|
||||||
if let Some(mut next) = egl.buffers.next_buffer.take() {
|
if let Some(mut next) = egl.buffers.next_buffer.take() {
|
||||||
|
@ -401,6 +391,9 @@ impl Drop for DrmBackend {
|
||||||
// ignore failure at this point
|
// ignore failure at this point
|
||||||
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle());
|
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore failure at this point
|
||||||
|
let _ = crtc::clear_cursor(&*graphics.context.devices.drm, crtc);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,13 +408,12 @@ impl GraphicsBackend for DrmBackend {
|
||||||
self.graphics.head().head().head(),
|
self.graphics.head().head().head(),
|
||||||
self.crtc,
|
self.crtc,
|
||||||
(x as i32, y as i32),
|
(x as i32, y as i32),
|
||||||
).chain_err(|| {
|
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
|
fn set_cursor_representation(
|
||||||
-> Result<()> {
|
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
|
||||||
|
) -> Result<()> {
|
||||||
let (w, h) = buffer.dimensions();
|
let (w, h) = buffer.dimensions();
|
||||||
|
|
||||||
debug!(self.logger, "Importing cursor");
|
debug!(self.logger, "Importing cursor");
|
||||||
|
@ -437,11 +429,11 @@ impl GraphicsBackend for DrmBackend {
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
GbmFormat::ARGB8888,
|
GbmFormat::ARGB8888,
|
||||||
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
|
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
|
||||||
)
|
)
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
cursor
|
cursor
|
||||||
.write(&*buffer.into_raw())
|
.write(&**buffer)
|
||||||
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
.chain_err(|| ErrorKind::GbmInitFailed)?;
|
||||||
|
|
||||||
trace!(self.logger, "Set the new imported cursor");
|
trace!(self.logger, "Set the new imported cursor");
|
||||||
|
@ -454,9 +446,9 @@ impl GraphicsBackend for DrmBackend {
|
||||||
(hotspot.0 as i32, hotspot.1 as i32),
|
(hotspot.0 as i32, hotspot.1 as i32),
|
||||||
).is_err()
|
).is_err()
|
||||||
{
|
{
|
||||||
crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err(|| {
|
crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err(
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
|
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
|
||||||
})?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// and store it
|
// and store it
|
||||||
|
@ -489,7 +481,10 @@ impl EGLGraphicsBackend for DrmBackend {
|
||||||
// would most likely result in a lot of flickering.
|
// would most likely result in a lot of flickering.
|
||||||
// neither weston, wlc or wlroots bother with that as well.
|
// neither weston, wlc or wlroots bother with that as well.
|
||||||
// so we just assume we got at least two buffers to do flipping
|
// so we just assume we got at least two buffers to do flipping
|
||||||
let mut next_bo = surface.gbm.lock_front_buffer().expect("Surface only has one front buffer. Not supported by smithay");
|
let mut next_bo = surface
|
||||||
|
.gbm
|
||||||
|
.lock_front_buffer()
|
||||||
|
.expect("Surface only has one front buffer. Not supported by smithay");
|
||||||
|
|
||||||
// create a framebuffer if the front buffer does not have one already
|
// create a framebuffer if the front buffer does not have one already
|
||||||
// (they are reused by gbm)
|
// (they are reused by gbm)
|
||||||
|
@ -497,7 +492,8 @@ impl EGLGraphicsBackend for DrmBackend {
|
||||||
let fb = if let Some(info) = maybe_fb {
|
let fb = if let Some(info) = maybe_fb {
|
||||||
info
|
info
|
||||||
} else {
|
} else {
|
||||||
let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo).map_err(|_| SwapBuffersError::ContextLost)?;
|
let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo)
|
||||||
|
.map_err(|_| SwapBuffersError::ContextLost)?;
|
||||||
next_bo.set_userdata(fb);
|
next_bo.set_userdata(fb);
|
||||||
fb
|
fb
|
||||||
};
|
};
|
||||||
|
@ -506,7 +502,12 @@ impl EGLGraphicsBackend for DrmBackend {
|
||||||
trace!(self.logger, "Queueing Page flip");
|
trace!(self.logger, "Queueing Page flip");
|
||||||
|
|
||||||
// and flip
|
// and flip
|
||||||
crtc::page_flip(graphics.context.devices.drm, self.crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent]).map_err(|_| SwapBuffersError::ContextLost)
|
crtc::page_flip(
|
||||||
|
graphics.context.devices.drm,
|
||||||
|
self.crtc,
|
||||||
|
fb.handle(),
|
||||||
|
&[crtc::PageFlipFlags::PageFlipEvent],
|
||||||
|
).map_err(|_| SwapBuffersError::ContextLost)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -523,7 +524,9 @@ impl EGLGraphicsBackend for DrmBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.graphics.head().rent(|context| context.is_current())
|
self.graphics.rent_all(|graphics| {
|
||||||
|
graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
|
|
@ -8,6 +8,11 @@ use rental::TryNewError;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
errors {
|
errors {
|
||||||
|
#[doc = "Unable to acquire drm master"]
|
||||||
|
DrmMasterFailed {
|
||||||
|
description("Failed to acquire drm master")
|
||||||
|
}
|
||||||
|
|
||||||
#[doc = "The `DrmDevice` encountered an access error"]
|
#[doc = "The `DrmDevice` encountered an access error"]
|
||||||
DrmDev(dev: String) {
|
DrmDev(dev: String) {
|
||||||
description("The drm device encountered an access error"),
|
description("The drm device encountered an access error"),
|
||||||
|
@ -26,7 +31,13 @@ error_chain! {
|
||||||
display("Swapping front buffers failed"),
|
display("Swapping front buffers failed"),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "mode is not compatible with all given connectors"]
|
#[doc = "Device is currently paused"]
|
||||||
|
DeviceInactive {
|
||||||
|
description("Device is currently paused, operation rejected"),
|
||||||
|
display("Device is currently paused, operation rejected"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Mode is not compatible with all given connectors"]
|
||||||
ModeNotSuitable(mode: Mode) {
|
ModeNotSuitable(mode: Mode) {
|
||||||
description("Mode is not compatible with all given connectors"),
|
description("Mode is not compatible with all given connectors"),
|
||||||
display("Mode ({:?}) is not compatible with all given connectors", mode),
|
display("Mode ({:?}) is not compatible with all given connectors", mode),
|
||||||
|
|
|
@ -90,11 +90,11 @@
|
||||||
//!
|
//!
|
||||||
//! // Create the backend
|
//! // Create the backend
|
||||||
//! let backend: StateToken<DrmBackend> = device.create_backend(
|
//! let backend: StateToken<DrmBackend> = device.create_backend(
|
||||||
//! &mut event_loop,
|
//! event_loop.state(),
|
||||||
//! crtc,
|
//! crtc,
|
||||||
//! mode,
|
//! mode,
|
||||||
//! vec![connector_info.handle()]
|
//! vec![connector_info.handle()]
|
||||||
//! ).unwrap();
|
//! ).unwrap().clone();
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -118,12 +118,13 @@
|
||||||
//! #
|
//! #
|
||||||
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||||
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||||
|
//! use drm::control::crtc::{Handle as CrtcHandle};
|
||||||
//! use drm::result::Error as DrmError;
|
//! use drm::result::Error as DrmError;
|
||||||
//! # use std::fs::OpenOptions;
|
//! # use std::fs::OpenOptions;
|
||||||
//! # use std::time::Duration;
|
//! # use std::time::Duration;
|
||||||
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
||||||
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
//! use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
//! use wayland_server::{EventLoopHandle, StateToken};
|
//! use wayland_server::{StateToken, StateProxy};
|
||||||
//! #
|
//! #
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! #
|
//! #
|
||||||
|
@ -145,27 +146,30 @@
|
||||||
//! # let crtc = res_handles.crtcs()[0];
|
//! # let crtc = res_handles.crtcs()[0];
|
||||||
//! # let mode = connector_info.modes()[0];
|
//! # let mode = connector_info.modes()[0];
|
||||||
//! # let backend: StateToken<DrmBackend> = device.create_backend(
|
//! # let backend: StateToken<DrmBackend> = device.create_backend(
|
||||||
//! # &mut event_loop,
|
//! # event_loop.state(),
|
||||||
//! # crtc,
|
//! # crtc,
|
||||||
//! # mode,
|
//! # mode,
|
||||||
//! # vec![connector_info.handle()]
|
//! # vec![connector_info.handle()]
|
||||||
//! # ).unwrap();
|
//! # ).unwrap().clone();
|
||||||
//!
|
//!
|
||||||
//! struct MyDrmHandler;
|
//! struct MyDrmHandler;
|
||||||
//!
|
//!
|
||||||
//! impl DrmHandler<DrmBackend> for MyDrmHandler {
|
//! impl DrmHandler<DrmBackend> for MyDrmHandler {
|
||||||
//! fn ready(&mut self,
|
//! fn ready<'a, S: Into<StateProxy<'a>>>(
|
||||||
//! evlh: &mut EventLoopHandle,
|
//! &mut self,
|
||||||
|
//! state: S,
|
||||||
//! _device: &mut DrmDevice<DrmBackend>,
|
//! _device: &mut DrmDevice<DrmBackend>,
|
||||||
//! backend: &StateToken<DrmBackend>,
|
//! backend: &StateToken<DrmBackend>,
|
||||||
|
//! _crtc: CrtcHandle,
|
||||||
//! _frame: u32,
|
//! _frame: u32,
|
||||||
//! _duration: Duration)
|
//! _duration: Duration)
|
||||||
//! {
|
//! {
|
||||||
//! // render surfaces and swap again
|
//! // render surfaces and swap again
|
||||||
//! evlh.state().get(backend).swap_buffers().unwrap();
|
//! state.into().get(backend).swap_buffers().unwrap();
|
||||||
//! }
|
//! }
|
||||||
//! fn error(&mut self,
|
//! fn error<'a, S: Into<StateProxy<'a>>>(
|
||||||
//! _: &mut EventLoopHandle,
|
//! &mut self,
|
||||||
|
//! _state: S,
|
||||||
//! device: &mut DrmDevice<DrmBackend>,
|
//! device: &mut DrmDevice<DrmBackend>,
|
||||||
//! error: DrmError)
|
//! error: DrmError)
|
||||||
//! {
|
//! {
|
||||||
|
@ -176,27 +180,35 @@
|
||||||
//! // render something (like clear_color)
|
//! // render something (like clear_color)
|
||||||
//! event_loop.state().get(&backend).swap_buffers().unwrap();
|
//! event_loop.state().get(&backend).swap_buffers().unwrap();
|
||||||
//!
|
//!
|
||||||
//! let _source = drm_device_bind(&mut event_loop, device, MyDrmHandler).unwrap();
|
//! let device_token = event_loop.state().insert(device);
|
||||||
|
//! let _source = drm_device_bind(&mut event_loop, device_token, MyDrmHandler).unwrap();
|
||||||
//!
|
//!
|
||||||
//! event_loop.run().unwrap();
|
//! event_loop.run().unwrap();
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements};
|
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements};
|
||||||
|
use backend::graphics::egl::EGLGraphicsBackend;
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
use backend::session::SessionObserver;
|
||||||
use drm::Device as BasicDevice;
|
use drm::Device as BasicDevice;
|
||||||
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
|
||||||
use drm::control::Device as ControlDevice;
|
use drm::control::Device as ControlDevice;
|
||||||
use drm::result::Error as DrmError;
|
use drm::result::Error as DrmError;
|
||||||
use gbm::Device as GbmDevice;
|
use gbm::Device as GbmDevice;
|
||||||
use nix;
|
use nix;
|
||||||
|
use nix::Result as NixResult;
|
||||||
|
use nix::unistd::close;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::mem;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use wayland_server::{EventLoopHandle, StateToken};
|
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
||||||
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
|
@ -207,11 +219,11 @@ use self::error::*;
|
||||||
|
|
||||||
/// Internal struct as required by the drm crate
|
/// Internal struct as required by the drm crate
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct DrmDev(File);
|
pub(crate) struct DrmDev(RawFd);
|
||||||
|
|
||||||
impl AsRawFd for DrmDev {
|
impl AsRawFd for DrmDev {
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
self.0.as_raw_fd()
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl BasicDevice for DrmDev {}
|
impl BasicDevice for DrmDev {}
|
||||||
|
@ -219,12 +231,11 @@ impl ControlDevice for DrmDev {}
|
||||||
|
|
||||||
impl DrmDev {
|
impl DrmDev {
|
||||||
unsafe fn new_from_fd(fd: RawFd) -> Self {
|
unsafe fn new_from_fd(fd: RawFd) -> Self {
|
||||||
use std::os::unix::io::FromRawFd;
|
DrmDev(fd)
|
||||||
DrmDev(File::from_raw_fd(fd))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_from_file(file: File) -> Self {
|
fn new_from_file(file: File) -> Self {
|
||||||
DrmDev(file)
|
DrmDev(file.into_raw_fd())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +267,8 @@ use self::devices::{Context, Devices};
|
||||||
pub struct DrmDevice<B: Borrow<DrmBackend> + 'static> {
|
pub struct DrmDevice<B: Borrow<DrmBackend> + 'static> {
|
||||||
context: Rc<Context>,
|
context: Rc<Context>,
|
||||||
backends: HashMap<crtc::Handle, StateToken<B>>,
|
backends: HashMap<crtc::Handle, StateToken<B>>,
|
||||||
|
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||||
|
active: bool,
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,6 +364,30 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
|
|
||||||
info!(log, "DrmDevice initializing");
|
info!(log, "DrmDevice initializing");
|
||||||
|
|
||||||
|
// we want to mode-set, so we better be the master
|
||||||
|
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
|
||||||
|
|
||||||
|
let mut old_state = HashMap::new();
|
||||||
|
let res_handles = drm.resource_handles()
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)))?;
|
||||||
|
for &con in res_handles.connectors() {
|
||||||
|
let con_info = connector::Info::load_from_device(&drm, con)
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)))?;
|
||||||
|
if let Some(enc) = con_info.current_encoder() {
|
||||||
|
let enc_info = encoder::Info::load_from_device(&drm, enc)
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)))?;
|
||||||
|
if let Some(crtc) = enc_info.current_crtc() {
|
||||||
|
let info = crtc::Info::load_from_device(&drm, crtc)
|
||||||
|
.chain_err(|| ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)))?;
|
||||||
|
old_state
|
||||||
|
.entry(crtc)
|
||||||
|
.or_insert((info, Vec::new()))
|
||||||
|
.1
|
||||||
|
.push(con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Open the gbm device from the drm device and create a context based on that
|
// Open the gbm device from the drm device and create a context based on that
|
||||||
Ok(DrmDevice {
|
Ok(DrmDevice {
|
||||||
context: Rc::new(Context::try_new(
|
context: Rc::new(Context::try_new(
|
||||||
|
@ -374,6 +411,8 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
},
|
},
|
||||||
)?),
|
)?),
|
||||||
backends: HashMap::new(),
|
backends: HashMap::new(),
|
||||||
|
old_state,
|
||||||
|
active: true,
|
||||||
logger: log,
|
logger: log,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -383,16 +422,21 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
///
|
///
|
||||||
/// Errors if initialization fails or the mode is not available on all given
|
/// Errors if initialization fails or the mode is not available on all given
|
||||||
/// connectors.
|
/// connectors.
|
||||||
pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
|
pub fn create_backend<'a, I, S>(
|
||||||
connectors: I)
|
&mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I
|
||||||
-> Result<StateToken<B>>
|
) -> Result<&StateToken<B>>
|
||||||
where
|
where
|
||||||
I: Into<Vec<connector::Handle>>,
|
I: Into<Vec<connector::Handle>>,
|
||||||
|
S: Into<StateProxy<'a>>,
|
||||||
{
|
{
|
||||||
if self.backends.contains_key(&crtc) {
|
if self.backends.contains_key(&crtc) {
|
||||||
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
bail!(ErrorKind::CrtcAlreadyInUse(crtc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.active {
|
||||||
|
bail!(ErrorKind::DeviceInactive);
|
||||||
|
}
|
||||||
|
|
||||||
// check if the given connectors and crtc match
|
// check if the given connectors and crtc match
|
||||||
let connectors = connectors.into();
|
let connectors = connectors.into();
|
||||||
|
|
||||||
|
@ -400,7 +444,10 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
for connector in &connectors {
|
for connector in &connectors {
|
||||||
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
|
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
|
||||||
.chain_err(|| {
|
.chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Loading connector info on {:?}",
|
||||||
|
self.context.head().head()
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// check the mode
|
// check the mode
|
||||||
|
@ -414,21 +461,26 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| {
|
.map(|encoder| {
|
||||||
encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| {
|
encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Loading encoder info on {:?}",
|
||||||
|
self.context.head().head()
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<encoder::Info>>>()?;
|
.collect::<Result<Vec<encoder::Info>>>()?;
|
||||||
|
|
||||||
// and if any encoder supports the selected crtc
|
// and if any encoder supports the selected crtc
|
||||||
let resource_handles = self.resource_handles().chain_err(|| {
|
let resource_handles = self.resource_handles().chain_err(|| {
|
||||||
ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
|
ErrorKind::DrmDev(format!(
|
||||||
|
"Loading drm resources on {:?}",
|
||||||
|
self.context.head().head()
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
if !encoders
|
if !encoders
|
||||||
.iter()
|
.iter()
|
||||||
.map(|encoder| encoder.possible_crtcs())
|
.map(|encoder| encoder.possible_crtcs())
|
||||||
.all(|crtc_list| {
|
.any(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&crtc))
|
||||||
resource_handles.filter_crtcs(crtc_list).contains(&crtc)
|
{
|
||||||
}) {
|
|
||||||
bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
|
bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,10 +489,44 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
|
|
||||||
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
|
||||||
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
|
||||||
let token = evlh.state().insert(backend.into());
|
self.backends
|
||||||
self.backends.insert(crtc, token.clone());
|
.insert(crtc, state.into().insert(backend.into()));
|
||||||
|
|
||||||
Ok(token)
|
Ok(self.backends.get(&crtc).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current backend for a given crtc if any
|
||||||
|
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
|
||||||
|
self.backends.get(crtc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all belonging backends
|
||||||
|
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
|
||||||
|
self.backends.values().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy the backend using a given crtc if any
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if the backend is already borrowed from the state
|
||||||
|
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
|
||||||
|
where
|
||||||
|
S: Into<StateProxy<'a>>,
|
||||||
|
{
|
||||||
|
if let Some(token) = self.backends.remove(crtc) {
|
||||||
|
state.into().remove(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the device
|
||||||
|
///
|
||||||
|
/// ## Warning
|
||||||
|
/// Never call this function if the device is managed by another backend e.g. the `UdevBackend`.
|
||||||
|
/// Only use this function for manually initialized devices.
|
||||||
|
pub fn close(self) -> NixResult<()> {
|
||||||
|
let fd = self.as_raw_fd();
|
||||||
|
mem::drop(self);
|
||||||
|
close(fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,9 +536,45 @@ impl<B: Borrow<DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
|
||||||
self.context.head().head().as_raw_fd()
|
self.context.head().head().as_raw_fd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
|
impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
|
||||||
impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
|
impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
|
||||||
|
|
||||||
|
impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if Rc::strong_count(&self.context) > 1 {
|
||||||
|
panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed");
|
||||||
|
}
|
||||||
|
for (handle, (info, connectors)) in self.old_state.drain() {
|
||||||
|
if let Err(err) = crtc::set(
|
||||||
|
self.context.head().head(),
|
||||||
|
handle,
|
||||||
|
info.fb(),
|
||||||
|
&connectors,
|
||||||
|
info.position(),
|
||||||
|
info.mode(),
|
||||||
|
) {
|
||||||
|
error!(
|
||||||
|
self.logger,
|
||||||
|
"Failed to reset crtc ({:?}). Error: {}", handle, err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(err) = self.drop_master() {
|
||||||
|
error!(
|
||||||
|
self.logger,
|
||||||
|
"Failed to drop drm master state. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: Borrow<DrmBackend> + 'static> Hash for DrmDevice<B> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.as_raw_fd().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handler for drm node events
|
/// Handler for drm node events
|
||||||
///
|
///
|
||||||
/// See module-level documentation for its use
|
/// See module-level documentation for its use
|
||||||
|
@ -462,61 +584,130 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
|
||||||
///
|
///
|
||||||
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
||||||
/// check using `DrmBackend::is`.
|
/// check using `DrmBackend::is`.
|
||||||
fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
///
|
||||||
frame: u32, duration: Duration);
|
/// ## Panics
|
||||||
|
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
||||||
|
/// and is not necessary as it is already provided via the `device` parameter.
|
||||||
|
fn ready<'a, S: Into<StateProxy<'a>>>(
|
||||||
|
&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>, crtc: crtc::Handle,
|
||||||
|
frame: u32, duration: Duration,
|
||||||
|
);
|
||||||
/// The `DrmDevice` has thrown an error.
|
/// The `DrmDevice` has thrown an error.
|
||||||
///
|
///
|
||||||
/// The related backends are most likely *not* usable anymore and
|
/// The related backends are most likely *not* usable anymore and
|
||||||
/// the whole stack has to be recreated.
|
/// the whole stack has to be recreated..
|
||||||
fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: DrmError);
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
||||||
|
/// and is not necessary as it is already provided via the `device` parameter.
|
||||||
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bind a `DrmDevice` to an `EventLoop`,
|
/// Bind a `DrmDevice` to an `EventLoop`,
|
||||||
///
|
///
|
||||||
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
/// This will cause it to recieve events and feed them into an `DrmHandler`
|
||||||
pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H)
|
pub fn drm_device_bind<B, H>(
|
||||||
-> IoResult<FdEventSource<(DrmDevice<B>, H)>>
|
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<B>>, handler: H
|
||||||
|
) -> IoResult<FdEventSource<(StateToken<DrmDevice<B>>, H)>>
|
||||||
where
|
where
|
||||||
B: Borrow<DrmBackend> + 'static,
|
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
H: DrmHandler<B> + 'static,
|
||||||
{
|
{
|
||||||
|
let fd = evlh.state().get(&device).as_raw_fd();
|
||||||
evlh.add_fd_event_source(
|
evlh.add_fd_event_source(
|
||||||
device.as_raw_fd(),
|
fd,
|
||||||
fd_event_source_implementation(),
|
fd_event_source_implementation(),
|
||||||
(device, handler),
|
(device, handler),
|
||||||
FdInterest::READ,
|
FdInterest::READ,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
|
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<B>>, H)>
|
||||||
where
|
where
|
||||||
B: Borrow<DrmBackend> + 'static,
|
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||||
H: DrmHandler<B> + 'static,
|
H: DrmHandler<B> + 'static,
|
||||||
{
|
{
|
||||||
FdEventSourceImpl {
|
FdEventSourceImpl {
|
||||||
ready: |evlh, id, _, _| {
|
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
|
||||||
let &mut (ref mut dev, ref mut handler) = id;
|
let (events, logger) = {
|
||||||
|
let dev = evlh.state().get(dev_token);
|
||||||
let events = crtc::receive_events(dev);
|
let events = crtc::receive_events(dev);
|
||||||
|
let logger = dev.logger.clone();
|
||||||
|
(events, logger)
|
||||||
|
};
|
||||||
|
|
||||||
match events {
|
match events {
|
||||||
Ok(events) => for event in events {
|
Ok(events) => for event in events {
|
||||||
if let crtc::Event::PageFlip(event) = event {
|
if let crtc::Event::PageFlip(event) = event {
|
||||||
let token = dev.backends.get(&event.crtc).cloned();
|
evlh.state().with_value(dev_token, |state, mut dev| {
|
||||||
if let Some(token) = token {
|
if dev.active {
|
||||||
|
if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() {
|
||||||
// we can now unlock the buffer
|
// we can now unlock the buffer
|
||||||
evlh.state().get(&token).borrow().unlock_buffer();
|
state.get(&backend_token).borrow().unlock_buffer();
|
||||||
trace!(dev.logger, "Handling event for backend {:?}", event.crtc);
|
trace!(logger, "Handling event for backend {:?}", event.crtc);
|
||||||
// and then call the user to render the next frame
|
// and then call the user to render the next frame
|
||||||
handler.ready(evlh, dev, &token, event.frame, event.duration);
|
handler.ready(
|
||||||
|
state,
|
||||||
|
&mut dev,
|
||||||
|
&backend_token,
|
||||||
|
event.crtc,
|
||||||
|
event.frame,
|
||||||
|
event.duration,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(err) => handler.error(evlh, dev, err),
|
Err(err) => evlh.state().with_value(dev_token, |state, mut dev| {
|
||||||
|
handler.error(state, &mut dev, err)
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
error: |evlh, id, _, error| {
|
error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| {
|
||||||
warn!(id.0.logger, "DrmDevice errored: {}", error);
|
evlh.state().with_value(dev_token, |state, mut dev| {
|
||||||
id.1.error(evlh, &mut id.0, error.into());
|
warn!(dev.logger, "DrmDevice errored: {}", error);
|
||||||
|
handler.error(state, &mut dev, error.into());
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B>> {
|
||||||
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
|
let device: &mut DrmDevice<B> = state.get_mut(self);
|
||||||
|
device.active = false;
|
||||||
|
if let Err(err) = device.drop_master() {
|
||||||
|
error!(
|
||||||
|
device.logger,
|
||||||
|
"Failed to drop drm master state. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
|
state.with_value(self, |state, device| {
|
||||||
|
device.active = true;
|
||||||
|
if let Err(err) = device.set_master() {
|
||||||
|
crit!(
|
||||||
|
device.logger,
|
||||||
|
"Failed to acquire drm master again. Error: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for token in device.backends.values() {
|
||||||
|
let backend = state.get(token);
|
||||||
|
if let Err(err) = backend.borrow().swap_buffers() {
|
||||||
|
// TODO handle this better?
|
||||||
|
error!(
|
||||||
|
device.logger,
|
||||||
|
"Failed to activate crtc ({:?}) again. Error: {}",
|
||||||
|
backend.borrow().crtc(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -238,9 +238,9 @@ pub struct EGLContext<'a, T: NativeSurface> {
|
||||||
impl<'a> EGLContext<'a, ()> {
|
impl<'a> EGLContext<'a, ()> {
|
||||||
/// Create a new context from a given `winit`-`Window`
|
/// Create a new context from a given `winit`-`Window`
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
pub fn new_from_winit<L>(window: &'a WinitWindow, attributes: GlAttributes,
|
pub fn new_from_winit<L>(
|
||||||
reqs: PixelFormatRequirements, logger: L)
|
window: &'a WinitWindow, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L
|
||||||
-> Result<EGLContext<'a, WinitWindow>>
|
) -> Result<EGLContext<'a, WinitWindow>>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -268,9 +268,9 @@ impl<'a> EGLContext<'a, ()> {
|
||||||
|
|
||||||
/// Create a new context from a given `gbm::Device`
|
/// Create a new context from a given `gbm::Device`
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
pub fn new_from_gbm<L, U: 'static>(gbm: &'a GbmDevice<'a>, attributes: GlAttributes,
|
pub fn new_from_gbm<L, U: 'static>(
|
||||||
reqs: PixelFormatRequirements, logger: L)
|
gbm: &'a GbmDevice<'a>, attributes: GlAttributes, reqs: PixelFormatRequirements, logger: L
|
||||||
-> Result<EGLContext<'a, GbmSurface<'a, U>>>
|
) -> Result<EGLContext<'a, GbmSurface<'a, U>>>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -288,9 +288,10 @@ impl<'a> EGLContext<'a, ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
unsafe fn new(native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
|
unsafe fn new(
|
||||||
log: ::slog::Logger)
|
native: NativeDisplayPtr, mut attributes: GlAttributes, reqs: PixelFormatRequirements,
|
||||||
-> Result<EGLContext<'a, T>>
|
log: ::slog::Logger,
|
||||||
|
) -> Result<EGLContext<'a, T>>
|
||||||
where
|
where
|
||||||
T: NativeSurface,
|
T: NativeSurface,
|
||||||
{
|
{
|
||||||
|
@ -322,8 +323,7 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
Some(version) => {
|
Some(version) => {
|
||||||
error!(
|
error!(
|
||||||
log,
|
log,
|
||||||
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend",
|
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend", version
|
||||||
version
|
|
||||||
);
|
);
|
||||||
bail!(ErrorKind::OpenGlVersionNotSupported(version));
|
bail!(ErrorKind::OpenGlVersionNotSupported(version));
|
||||||
}
|
}
|
||||||
|
@ -423,9 +423,9 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeDisplayPtr::X11(display) |
|
NativeDisplayPtr::X11(display)
|
||||||
NativeDisplayPtr::Gbm(display) |
|
| NativeDisplayPtr::Gbm(display)
|
||||||
NativeDisplayPtr::Wayland(display) => {
|
| NativeDisplayPtr::Wayland(display) => {
|
||||||
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||||
egl.GetDisplay(display as *mut _)
|
egl.GetDisplay(display as *mut _)
|
||||||
}
|
}
|
||||||
|
@ -727,9 +727,9 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
self.display,
|
self.display,
|
||||||
self.config_id,
|
self.config_id,
|
||||||
match surface {
|
match surface {
|
||||||
NativeSurfacePtr::X11(ptr) |
|
NativeSurfacePtr::X11(ptr)
|
||||||
NativeSurfacePtr::Wayland(ptr) |
|
| NativeSurfacePtr::Wayland(ptr)
|
||||||
NativeSurfacePtr::Gbm(ptr) => ptr,
|
| NativeSurfacePtr::Gbm(ptr) => ptr,
|
||||||
},
|
},
|
||||||
self.surface_attributes.as_ptr(),
|
self.surface_attributes.as_ptr(),
|
||||||
)
|
)
|
||||||
|
@ -817,7 +817,7 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
match unsafe { self.context.egl.GetError() } as u32 {
|
match unsafe { self.context.egl.GetError() } as u32 {
|
||||||
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
|
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
|
||||||
err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err),
|
err => Err(SwapBuffersError::Unknown(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -847,6 +847,14 @@ impl<'context, 'surface, T: NativeSurface> EGLSurface<'context, 'surface, T> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the OpenGL surface is the current one in the thread.
|
||||||
|
pub fn is_current(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
self.context.egl.GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _
|
||||||
|
&& self.context.egl.GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a, 'b, T: NativeSurface> Send for EGLSurface<'a, 'b, T> {}
|
unsafe impl<'a, 'b, T: NativeSurface> Send for EGLSurface<'a, 'b, T> {}
|
||||||
|
@ -882,6 +890,8 @@ pub enum SwapBuffersError {
|
||||||
/// This error can be returned when `swap_buffers` has been called multiple times
|
/// This error can be returned when `swap_buffers` has been called multiple times
|
||||||
/// without any modification in between.
|
/// without any modification in between.
|
||||||
AlreadySwapped,
|
AlreadySwapped,
|
||||||
|
/// Unknown GL error
|
||||||
|
Unknown(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SwapBuffersError {
|
impl fmt::Display for SwapBuffersError {
|
||||||
|
@ -898,6 +908,7 @@ impl error::Error for SwapBuffersError {
|
||||||
SwapBuffersError::AlreadySwapped => {
|
SwapBuffersError::AlreadySwapped => {
|
||||||
"Buffers are already swapped, swap_buffers was called too many times"
|
"Buffers are already swapped, swap_buffers was called too many times"
|
||||||
}
|
}
|
||||||
|
SwapBuffersError::Unknown(_) => "Unknown Open GL error occurred",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ impl From<SwapBuffersError> for GliumSwapBuffersError {
|
||||||
match error {
|
match error {
|
||||||
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
|
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
|
||||||
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
|
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
|
||||||
|
SwapBuffersError::Unknown(_) => GliumSwapBuffersError::ContextLost, // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,9 @@ pub trait GraphicsBackend {
|
||||||
/// The format is entirely dictated by the concrete implementation and might range
|
/// The format is entirely dictated by the concrete implementation and might range
|
||||||
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
/// from raw image buffers over a fixed list of possible cursor types to simply the
|
||||||
/// void type () to represent no possible customization of the cursor itself.
|
/// void type () to represent no possible customization of the cursor itself.
|
||||||
fn set_cursor_representation(&self, cursor: Self::CursorFormat, hotspot: (u32, u32))
|
fn set_cursor_representation(
|
||||||
-> Result<(), Self::Error>;
|
&self, cursor: &Self::CursorFormat, hotspot: (u32, u32)
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod software;
|
pub mod software;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Common traits and types used for software rendering on graphics backends
|
//! Common traits and types used for software rendering on graphics backends
|
||||||
|
|
||||||
|
|
||||||
use super::GraphicsBackend;
|
use super::GraphicsBackend;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use wayland_server::protocol::wl_shm::Format;
|
use wayland_server::protocol::wl_shm::Format;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Common traits for input backends to receive input from.
|
//! Common traits for input backends to receive input from.
|
||||||
|
|
||||||
use backend::{SeatInternal, TouchSlotInternal};
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
/// A seat describes a group of input devices and at least one
|
/// A seat describes a group of input devices and at least one
|
||||||
|
@ -20,20 +19,18 @@ pub struct Seat {
|
||||||
capabilities: SeatCapabilities,
|
capabilities: SeatCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeatInternal for Seat {
|
impl Seat {
|
||||||
fn new(id: u64, capabilities: SeatCapabilities) -> Seat {
|
pub(crate) fn new(id: u64, capabilities: SeatCapabilities) -> Seat {
|
||||||
Seat {
|
Seat {
|
||||||
id: id,
|
id: id,
|
||||||
capabilities: capabilities,
|
capabilities: capabilities,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capabilities_mut(&mut self) -> &mut SeatCapabilities {
|
pub(crate) fn capabilities_mut(&mut self) -> &mut SeatCapabilities {
|
||||||
&mut self.capabilities
|
&mut self.capabilities
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Seat {
|
|
||||||
/// Get the currently capabilities of this `Seat`
|
/// Get the currently capabilities of this `Seat`
|
||||||
pub fn capabilities(&self) -> &SeatCapabilities {
|
pub fn capabilities(&self) -> &SeatCapabilities {
|
||||||
&self.capabilities
|
&self.capabilities
|
||||||
|
@ -318,8 +315,8 @@ pub struct TouchSlot {
|
||||||
id: u64,
|
id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TouchSlotInternal for TouchSlot {
|
impl TouchSlot {
|
||||||
fn new(id: u64) -> Self {
|
pub(crate) fn new(id: u64) -> Self {
|
||||||
TouchSlot { id: id }
|
TouchSlot { id: id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
//! Implementation of input backend trait for types provided by `libinput`
|
//! Implementation of input backend trait for types provided by `libinput`
|
||||||
|
|
||||||
use backend::{SeatInternal, TouchSlotInternal};
|
|
||||||
use backend::input as backend;
|
use backend::input as backend;
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
use backend::session::{AsErrno, Session, SessionObserver};
|
||||||
use input as libinput;
|
use input as libinput;
|
||||||
use input::event;
|
use input::event;
|
||||||
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
|
use std::collections::hash_map::{DefaultHasher, Entry, HashMap};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::Error as IoError;
|
use std::io::{Error as IoError, Result as IoResult};
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use wayland_server::{EventLoopHandle, StateProxy};
|
||||||
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
/// Libinput based `InputBackend`.
|
/// Libinput based `InputBackend`.
|
||||||
///
|
///
|
||||||
|
@ -261,7 +266,7 @@ impl backend::InputBackend for LibinputInputBackend {
|
||||||
if self.handler.is_some() {
|
if self.handler.is_some() {
|
||||||
self.clear_handler();
|
self.clear_handler();
|
||||||
}
|
}
|
||||||
info!(self.logger, "New input handler set.");
|
info!(self.logger, "New input handler set");
|
||||||
for seat in self.seats.values() {
|
for seat in self.seats.values() {
|
||||||
trace!(self.logger, "Calling on_seat_created with {:?}", seat);
|
trace!(self.logger, "Calling on_seat_created with {:?}", seat);
|
||||||
handler.on_seat_created(seat);
|
handler.on_seat_created(seat);
|
||||||
|
@ -549,3 +554,71 @@ impl From<event::pointer::ButtonState> for backend::MouseButtonState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
impl SessionObserver for libinput::Libinput {
|
||||||
|
fn pause<'a>(&mut self, _state: &mut StateProxy<'a>) {
|
||||||
|
self.suspend()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate<'a>(&mut self, _state: &mut StateProxy<'a>) {
|
||||||
|
// TODO Is this the best way to handle this failure?
|
||||||
|
self.resume().expect("Unable to resume libinput context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for types implementing the `Session` trait to provide
|
||||||
|
/// a `LibinputInterface` implementation.
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
pub struct LibinputSessionInterface<S: Session>(S);
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
impl<S: Session> From<S> for LibinputSessionInterface<S> {
|
||||||
|
fn from(session: S) -> LibinputSessionInterface<S> {
|
||||||
|
LibinputSessionInterface(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
|
impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
|
||||||
|
fn open_restricted(&mut self, path: &Path, flags: i32) -> Result<RawFd, i32> {
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
self.0
|
||||||
|
.open(path, OFlag::from_bits_truncate(flags))
|
||||||
|
.map_err(|err| err.as_errno().unwrap_or(1 /*Use EPERM by default*/))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_restricted(&mut self, fd: RawFd) {
|
||||||
|
let _ = self.0.close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds a `LibinputInputBackend` to a given `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Automatically feeds the backend with incoming events without any manual calls to
|
||||||
|
/// `dispatch_new_events`. Should be used to achieve the smallest possible latency.
|
||||||
|
pub fn libinput_bind(
|
||||||
|
backend: LibinputInputBackend, evlh: &mut EventLoopHandle
|
||||||
|
) -> IoResult<FdEventSource<LibinputInputBackend>> {
|
||||||
|
let fd = unsafe { backend.context.fd() };
|
||||||
|
evlh.add_fd_event_source(
|
||||||
|
fd,
|
||||||
|
fd_event_source_implementation(),
|
||||||
|
backend,
|
||||||
|
FdInterest::READ,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fd_event_source_implementation() -> FdEventSourceImpl<LibinputInputBackend> {
|
||||||
|
FdEventSourceImpl {
|
||||||
|
ready: |_evlh, ref mut backend, _, _| {
|
||||||
|
use backend::input::InputBackend;
|
||||||
|
if let Err(error) = backend.dispatch_new_events() {
|
||||||
|
warn!(backend.logger, "Libinput errored: {}", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: |_evlh, ref backend, _, error| {
|
||||||
|
warn!(backend.logger, "Libinput fd errored: {}", error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,14 +23,7 @@ pub mod winit;
|
||||||
pub mod drm;
|
pub mod drm;
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
pub mod libinput;
|
pub mod libinput;
|
||||||
|
#[cfg(feature = "backend_session")]
|
||||||
// Internal functions that need to be accessible by the different backend implementations
|
pub mod session;
|
||||||
|
#[cfg(feature = "backend_udev")]
|
||||||
trait SeatInternal {
|
pub mod udev;
|
||||||
fn new(id: u64, capabilities: input::SeatCapabilities) -> Self;
|
|
||||||
fn capabilities_mut(&mut self) -> &mut input::SeatCapabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
trait TouchSlotInternal {
|
|
||||||
fn new(id: u64) -> Self;
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,465 @@
|
||||||
|
//!
|
||||||
|
//! Implementation of the `Session` trait through the legacy vt kernel interface.
|
||||||
|
//!
|
||||||
|
//! This requires write permissions for the given tty device and any devices opened through this
|
||||||
|
//! interface. This means it will almost certainly require root permissions and not allow to run
|
||||||
|
//! the compositor as an unpriviledged user. Use this session type *only* as a fallback or for testing,
|
||||||
|
//! if anything better is available.
|
||||||
|
//!
|
||||||
|
//! ## How to use it
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize the session you may pass the path to any tty device, that shall be used.
|
||||||
|
//! If no path is given the tty used to start this compositor (if any) will be used.
|
||||||
|
//! A new session and its notifier will be returned.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! extern crate smithay;
|
||||||
|
//!
|
||||||
|
//! use smithay::backend::session::direct::DirectSession;
|
||||||
|
//!
|
||||||
|
//! # fn main() {
|
||||||
|
//! let (session, mut notifier) = DirectSession::new(None, None).unwrap();
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session
|
||||||
|
//!
|
||||||
|
//! The session may be used to open devices manually through the `Session` interface
|
||||||
|
//! or be passed to other object that need to open devices themselves.
|
||||||
|
//!
|
||||||
|
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
||||||
|
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
||||||
|
//!
|
||||||
|
//! In case you want to pass the same `Session` to multiple objects, `Session` is implement for
|
||||||
|
//! every `Rc<RefCell<Session>>` or `Arc<Mutex<Session>>`.
|
||||||
|
//!
|
||||||
|
//! ### Usage of the session notifier
|
||||||
|
//!
|
||||||
|
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
|
||||||
|
//! switching the tty via `DirectSession::change_vt`) and to automatically enable it again,
|
||||||
|
//! when the session becomes active again.
|
||||||
|
//!
|
||||||
|
//! It is crutial to avoid errors during that state. Examples for object that might be registered
|
||||||
|
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
|
||||||
|
//! automatically by the `UdevBackend`, if not done manually).
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use super::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||||
|
use nix::{Error as NixError, Result as NixResult};
|
||||||
|
use nix::fcntl::{self, open, OFlag};
|
||||||
|
use nix::libc::c_int;
|
||||||
|
use nix::sys::signal::{self, Signal};
|
||||||
|
use nix::sys::stat::{dev_t, fstat, major, minor, Mode};
|
||||||
|
use nix::unistd::{close, dup};
|
||||||
|
use std::io::Result as IoResult;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
#[cfg(feature = "backend_session_udev")]
|
||||||
|
use udev::Context;
|
||||||
|
use wayland_server::EventLoopHandle;
|
||||||
|
use wayland_server::sources::SignalEventSource;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
mod tty {
|
||||||
|
ioctl!(bad read kd_get_mode with 0x4B3B; i16);
|
||||||
|
ioctl!(bad write_int kd_set_mode with 0x4B3A);
|
||||||
|
pub const KD_TEXT: i16 = 0x00;
|
||||||
|
pub const KD_GRAPHICS: i16 = 0x00;
|
||||||
|
|
||||||
|
ioctl!(bad read kd_get_kb_mode with 0x4B44; i32);
|
||||||
|
ioctl!(bad write_int kd_set_kb_mode with 0x4B45);
|
||||||
|
pub const K_RAW: i32 = 0x00;
|
||||||
|
pub const K_XLATE: i32 = 0x01;
|
||||||
|
pub const K_MEDIUMRAW: i32 = 0x02;
|
||||||
|
pub const K_UNICODE: i32 = 0x03;
|
||||||
|
pub const K_OFF: i32 = 0x04;
|
||||||
|
|
||||||
|
ioctl!(bad write_int vt_activate with 0x5606);
|
||||||
|
ioctl!(bad write_int vt_wait_active with 0x5607);
|
||||||
|
ioctl!(bad write_ptr vt_set_mode with 0x5602; VtMode);
|
||||||
|
ioctl!(bad write_int vt_rel_disp with 0x5605);
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub struct VtMode {
|
||||||
|
/// vt mode
|
||||||
|
pub mode: i8,
|
||||||
|
/// if set, hang on writes if not active
|
||||||
|
pub waitv: i8,
|
||||||
|
/// signal to raise on release req
|
||||||
|
pub relsig: i16,
|
||||||
|
/// signal to raise on acquisition
|
||||||
|
pub acqsig: i16,
|
||||||
|
/// unused (set to 0)
|
||||||
|
pub frsig: i16,
|
||||||
|
}
|
||||||
|
pub const VT_AUTO: i8 = 0x00;
|
||||||
|
pub const VT_PROCESS: i8 = 0x01;
|
||||||
|
pub const VT_ACKACQ: i32 = 0x02;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn __libc_current_sigrtmin() -> i8;
|
||||||
|
pub fn __libc_current_sigrtmax() -> i8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
const TTY_MAJOR: u64 = 4;
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
const TTY_MAJOR: u64 = 0;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "backend_session_udev"))]
|
||||||
|
fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool {
|
||||||
|
major(dev) == TTY_MAJOR
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "backend_session_udev")]
|
||||||
|
fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool {
|
||||||
|
match path {
|
||||||
|
Some(path) => {
|
||||||
|
let udev = match Context::new() {
|
||||||
|
Ok(context) => context,
|
||||||
|
Err(_) => return major(dev) == TTY_MAJOR || minor(dev) != 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device = match udev.device_from_syspath(path) {
|
||||||
|
Ok(device) => device,
|
||||||
|
Err(_) => return major(dev) == TTY_MAJOR || minor(dev) != 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = if let Some(subsystem) = device.subsystem() {
|
||||||
|
subsystem == "tty"
|
||||||
|
} else {
|
||||||
|
major(dev) == TTY_MAJOR
|
||||||
|
};
|
||||||
|
res || minor(dev) != 0
|
||||||
|
}
|
||||||
|
None => major(dev) == TTY_MAJOR || minor(dev) != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Session` via the virtual terminal direct kernel interface
|
||||||
|
pub struct DirectSession {
|
||||||
|
tty: RawFd,
|
||||||
|
active: Arc<AtomicBool>,
|
||||||
|
vt: i32,
|
||||||
|
old_keyboard_mode: i32,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SessionNotifier` via the virtual terminal direct kernel interface
|
||||||
|
pub struct DirectSessionNotifier {
|
||||||
|
tty: RawFd,
|
||||||
|
active: Arc<AtomicBool>,
|
||||||
|
signals: Vec<Option<Box<SessionObserver>>>,
|
||||||
|
signal: Signal,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectSession {
|
||||||
|
/// Tries to creates a new session via the legacy virtual terminal interface.
|
||||||
|
///
|
||||||
|
/// If you do not provide a tty device path, it will try to open the currently active tty if any.
|
||||||
|
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let logger = ::slog_or_stdlog(logger)
|
||||||
|
.new(o!("smithay_module" => "backend_session", "session_type" => "direct/vt"));
|
||||||
|
|
||||||
|
let fd = tty.map(|path| {
|
||||||
|
open(path, fcntl::O_RDWR | fcntl::O_CLOEXEC, Mode::empty())
|
||||||
|
.chain_err(|| ErrorKind::FailedToOpenTTY(String::from(path.to_string_lossy())))
|
||||||
|
}).unwrap_or(dup(0 /*stdin*/).chain_err(|| ErrorKind::FailedToOpenTTY(String::from("<stdin>"))))?;
|
||||||
|
|
||||||
|
let active = Arc::new(AtomicBool::new(true));
|
||||||
|
|
||||||
|
match DirectSession::setup_tty(tty, fd, logger.clone()) {
|
||||||
|
Ok((vt, old_keyboard_mode, signal)) => Ok((
|
||||||
|
DirectSession {
|
||||||
|
tty: fd,
|
||||||
|
active: active.clone(),
|
||||||
|
vt,
|
||||||
|
old_keyboard_mode,
|
||||||
|
logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session")),
|
||||||
|
},
|
||||||
|
DirectSessionNotifier {
|
||||||
|
tty: fd,
|
||||||
|
active,
|
||||||
|
signals: Vec::new(),
|
||||||
|
signal,
|
||||||
|
logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Err(err) => {
|
||||||
|
let _ = close(fd);
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_tty(path: Option<&Path>, tty: RawFd, logger: ::slog::Logger) -> Result<(i32, i32, Signal)> {
|
||||||
|
let stat = fstat(tty).chain_err(|| ErrorKind::NotRunningFromTTY)?;
|
||||||
|
if !is_tty_device(stat.st_dev, path) {
|
||||||
|
bail!(ErrorKind::NotRunningFromTTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vt_num = minor(stat.st_rdev) as i32;
|
||||||
|
info!(logger, "Running from tty: {}", vt_num);
|
||||||
|
|
||||||
|
let mut mode = 0;
|
||||||
|
unsafe {
|
||||||
|
tty::kd_get_mode(tty, &mut mode).chain_err(|| ErrorKind::NotRunningFromTTY)?;
|
||||||
|
}
|
||||||
|
if mode != tty::KD_TEXT {
|
||||||
|
bail!(ErrorKind::TTYAlreadyInGraphicsMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
tty::vt_activate(tty, vt_num as c_int).chain_err(|| ErrorKind::FailedToActivateTTY(vt_num))?;
|
||||||
|
tty::vt_wait_active(tty, vt_num as c_int).chain_err(|| ErrorKind::FailedToWaitForTTY(vt_num))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut old_keyboard_mode = 0;
|
||||||
|
unsafe {
|
||||||
|
tty::kd_get_kb_mode(tty, &mut old_keyboard_mode)
|
||||||
|
.chain_err(|| ErrorKind::FailedToSaveTTYState(vt_num))?;
|
||||||
|
tty::kd_set_kb_mode(tty, tty::K_OFF).chain_err(|| ErrorKind::FailedToSetTTYKbMode(vt_num))?;
|
||||||
|
tty::kd_set_mode(tty, tty::KD_GRAPHICS as i32)
|
||||||
|
.chain_err(|| ErrorKind::FailedToSetTTYMode(vt_num))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support realtime signals
|
||||||
|
// https://github.com/nix-rust/nix/issues/495
|
||||||
|
/*
|
||||||
|
let signal = if tty::__libc_current_sigrtmin() > tty::__libc_current_sigrtmax() {
|
||||||
|
warn!(logger, "Not enough real-time signals available, falling back to USR1");
|
||||||
|
nix::sys::signal::SIGUSR2 as i32
|
||||||
|
} else {
|
||||||
|
tty::__libc_current_sigrtmin()
|
||||||
|
};*/
|
||||||
|
let signal = signal::SIGUSR2;
|
||||||
|
|
||||||
|
let mode = tty::VtMode {
|
||||||
|
mode: tty::VT_PROCESS,
|
||||||
|
relsig: signal as i16,
|
||||||
|
acqsig: signal as i16,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
tty::vt_set_mode(tty, &mode).chain_err(|| ErrorKind::FailedToTakeControlOfTTY(vt_num))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((vt_num, old_keyboard_mode, signal))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of the virtual terminal used by this session
|
||||||
|
pub fn vt(&self) -> i32 {
|
||||||
|
self.vt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session for DirectSession {
|
||||||
|
type Error = NixError;
|
||||||
|
|
||||||
|
fn open(&mut self, path: &Path, flags: OFlag) -> NixResult<RawFd> {
|
||||||
|
debug!(self.logger, "Opening device: {:?}", path);
|
||||||
|
let fd = open(path, flags, Mode::empty())?;
|
||||||
|
trace!(self.logger, "Fd num: {:?}", fd);
|
||||||
|
Ok(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fd: RawFd) -> NixResult<()> {
|
||||||
|
debug!(self.logger, "Closing device: {:?}", fd);
|
||||||
|
close(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.active.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat(&self) -> String {
|
||||||
|
// The VT api can only be used on seat0
|
||||||
|
String::from("seat0")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_vt(&mut self, vt_num: i32) -> NixResult<()> {
|
||||||
|
unsafe { tty::vt_activate(self.tty, vt_num).map(|_| ()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsErrno for NixError {
|
||||||
|
fn as_errno(&self) -> Option<i32> {
|
||||||
|
match *self {
|
||||||
|
NixError::Sys(errno) => Some(errno as i32),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DirectSession {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
info!(self.logger, "Deallocating tty {}", self.tty);
|
||||||
|
|
||||||
|
if let Err(err) = unsafe { tty::kd_set_kb_mode(self.tty, self.old_keyboard_mode) } {
|
||||||
|
warn!(
|
||||||
|
self.logger,
|
||||||
|
"Unable to restore vt keyboard mode. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Err(err) = unsafe { tty::kd_set_mode(self.tty, tty::KD_TEXT as i32) } {
|
||||||
|
warn!(
|
||||||
|
self.logger,
|
||||||
|
"Unable to restore vt text mode. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Err(err) = unsafe {
|
||||||
|
tty::vt_set_mode(
|
||||||
|
self.tty,
|
||||||
|
&tty::VtMode {
|
||||||
|
mode: tty::VT_AUTO,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
error!(self.logger, "Failed to reset vt handling. Error: {}", err);
|
||||||
|
}
|
||||||
|
if let Err(err) = close(self.tty) {
|
||||||
|
error!(
|
||||||
|
self.logger,
|
||||||
|
"Failed to close tty file descriptor. Error: {}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ids of registered `SessionObserver`s of the `DirectSessionNotifier`
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Id(usize);
|
||||||
|
|
||||||
|
impl SessionNotifier for DirectSessionNotifier {
|
||||||
|
type Id = Id;
|
||||||
|
|
||||||
|
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Id {
|
||||||
|
self.signals.push(Some(Box::new(signal)));
|
||||||
|
Id(self.signals.len() - 1)
|
||||||
|
}
|
||||||
|
fn unregister(&mut self, signal: Id) {
|
||||||
|
self.signals[signal.0] = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.active.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
fn seat(&self) -> &str {
|
||||||
|
"seat0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state.
|
||||||
|
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
|
||||||
|
/// session state.
|
||||||
|
pub fn direct_session_bind<L>(
|
||||||
|
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L
|
||||||
|
) -> IoResult<SignalEventSource<DirectSessionNotifier>>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let signal = notifier.signal;
|
||||||
|
|
||||||
|
evlh.add_signal_event_source(
|
||||||
|
|evlh, notifier, _| {
|
||||||
|
if notifier.is_active() {
|
||||||
|
info!(notifier.logger, "Session shall become inactive");
|
||||||
|
for signal in &mut notifier.signals {
|
||||||
|
if let &mut Some(ref mut signal) = signal {
|
||||||
|
signal.pause(&mut evlh.state().as_proxy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifier.active.store(false, Ordering::SeqCst);
|
||||||
|
unsafe {
|
||||||
|
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
|
||||||
|
}
|
||||||
|
debug!(notifier.logger, "Session is now inactive");
|
||||||
|
} else {
|
||||||
|
debug!(notifier.logger, "Session will become active again");
|
||||||
|
unsafe {
|
||||||
|
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
|
||||||
|
}
|
||||||
|
for signal in &mut notifier.signals {
|
||||||
|
if let &mut Some(ref mut signal) = signal {
|
||||||
|
signal.activate(&mut evlh.state().as_proxy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifier.active.store(true, Ordering::SeqCst);
|
||||||
|
info!(notifier.logger, "Session is now active again");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notifier,
|
||||||
|
signal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
#[doc = "Failed to open tty"]
|
||||||
|
FailedToOpenTTY(path: String) {
|
||||||
|
description("Failed to open tty"),
|
||||||
|
display("Failed to open tty ({:?})", path),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Not running from a tty"]
|
||||||
|
NotRunningFromTTY {
|
||||||
|
description("Not running from a tty"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "tty is already in KB_GRAPHICS mode"]
|
||||||
|
TTYAlreadyInGraphicsMode {
|
||||||
|
description("The tty is already in KB_GRAPHICS mode"),
|
||||||
|
display("The tty is already in graphics mode, is already a compositor running?"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to activate open tty"]
|
||||||
|
FailedToActivateTTY(num: i32) {
|
||||||
|
description("Failed to activate open tty"),
|
||||||
|
display("Failed to activate open tty ({:?})", num),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to wait for tty to become active"]
|
||||||
|
FailedToWaitForTTY(num: i32) {
|
||||||
|
description("Failed to wait for tty to become active"),
|
||||||
|
display("Failed to wait for tty ({:?}) to become active", num),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to save old tty state"]
|
||||||
|
FailedToSaveTTYState(num: i32) {
|
||||||
|
description("Failed to save old tty state"),
|
||||||
|
display("Failed to save old tty ({:?}) state", num),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to set tty kb mode"]
|
||||||
|
FailedToSetTTYKbMode(num: i32) {
|
||||||
|
description("Failed to set tty kb mode to K_OFF"),
|
||||||
|
display("Failed to set tty ({:?}) kb mode to K_OFF", num),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to set tty mode"]
|
||||||
|
FailedToSetTTYMode(num: i32) {
|
||||||
|
description("Failed to set tty mode to KD_GRAPHICS"),
|
||||||
|
display("Failed to set tty ({:?}) mode into graphics mode", num),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to set tty in process mode"]
|
||||||
|
FailedToTakeControlOfTTY(num: i32) {
|
||||||
|
description("Failed to set tty mode to VT_PROCESS"),
|
||||||
|
display("Failed to take control of tty ({:?})", num),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
//!
|
||||||
|
//! Abstraction of different session apis.
|
||||||
|
//!
|
||||||
|
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
|
||||||
|
//! mechanisms to switch between and handle device access and permissions for every running
|
||||||
|
//! instance.
|
||||||
|
//!
|
||||||
|
//! They are crutial to allow unpriviledged processes to use graphical or input devices.
|
||||||
|
//!
|
||||||
|
//! The following mechanisms are currently provided:
|
||||||
|
//! - direct - legacy tty / virtual terminal kernel api
|
||||||
|
//!
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use wayland_server::StateProxy;
|
||||||
|
|
||||||
|
/// General session interface.
|
||||||
|
///
|
||||||
|
/// Provides a way to open and close devices and change the active vt.
|
||||||
|
pub trait Session {
|
||||||
|
/// Error type of the implementation
|
||||||
|
type Error: AsErrno;
|
||||||
|
|
||||||
|
/// Opens a device at the given `path` with the given flags.
|
||||||
|
///
|
||||||
|
/// Returns a raw file descriptor
|
||||||
|
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
|
||||||
|
/// Close a previously opened file descriptor
|
||||||
|
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Change the currently active virtual terminal
|
||||||
|
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Check if this session is currently active
|
||||||
|
fn is_active(&self) -> bool;
|
||||||
|
/// Which seat this session is on
|
||||||
|
fn seat(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interface for registering for notifications for a given session.
|
||||||
|
///
|
||||||
|
/// Part of the session api which allows to get notified, when the given session
|
||||||
|
/// gets paused or becomes active again. Any object implementing the `SessionObserver` trait
|
||||||
|
/// may be registered.
|
||||||
|
pub trait SessionNotifier {
|
||||||
|
/// Id type of registered observers
|
||||||
|
type Id: PartialEq + Eq;
|
||||||
|
|
||||||
|
/// Registers a given `SessionObserver`.
|
||||||
|
///
|
||||||
|
/// Returns an id of the inserted observer, can be used to remove it again.
|
||||||
|
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id;
|
||||||
|
/// Removes an observer by its given id from `SessionNotifier::register`.
|
||||||
|
fn unregister(&mut self, signal: Self::Id);
|
||||||
|
|
||||||
|
/// Check if this session is currently active
|
||||||
|
fn is_active(&self) -> bool;
|
||||||
|
/// Which seat this session is on
|
||||||
|
fn seat(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait describing the ability to be notified when the session pauses or becomes active again.
|
||||||
|
///
|
||||||
|
/// It might be impossible to interact with devices while the session is disabled.
|
||||||
|
/// This interface provides callbacks for when that happens.
|
||||||
|
pub trait SessionObserver {
|
||||||
|
/// Session is about to be paused.
|
||||||
|
///
|
||||||
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
|
/// is provided via a `StateProxy`.
|
||||||
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>);
|
||||||
|
/// Session got active again
|
||||||
|
///
|
||||||
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
|
/// is provided via a `StateProxy`.
|
||||||
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session for () {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn open(&mut self, _path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
fn close(&mut self, _fd: RawFd) -> Result<(), Self::Error> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_vt(&mut self, _vt: i32) -> Result<(), Self::Error> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn seat(&self) -> String {
|
||||||
|
String::from("seat0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Session> Session for Rc<RefCell<S>> {
|
||||||
|
type Error = S::Error;
|
||||||
|
|
||||||
|
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
|
||||||
|
self.borrow_mut().open(path, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
|
||||||
|
self.borrow_mut().close(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
|
||||||
|
self.borrow_mut().change_vt(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.borrow().is_active()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat(&self) -> String {
|
||||||
|
self.borrow().seat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Session> Session for Arc<Mutex<S>> {
|
||||||
|
type Error = S::Error;
|
||||||
|
|
||||||
|
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error> {
|
||||||
|
self.lock().unwrap().open(path, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
|
||||||
|
self.lock().unwrap().close(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
|
||||||
|
self.lock().unwrap().change_vt(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.lock().unwrap().is_active()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat(&self) -> String {
|
||||||
|
self.lock().unwrap().seat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows errors to be described by an error number
|
||||||
|
pub trait AsErrno: ::std::fmt::Debug {
|
||||||
|
/// Returns the error number representing this error if any
|
||||||
|
fn as_errno(&self) -> Option<i32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsErrno for () {
|
||||||
|
fn as_errno(&self) -> Option<i32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod direct;
|
|
@ -0,0 +1,466 @@
|
||||||
|
//!
|
||||||
|
//! Provides `udev` related functionality for automated device scanning.
|
||||||
|
//!
|
||||||
|
//! This module mainly provides the `UdevBackend`, which constantly monitors available drm devices
|
||||||
|
//! and notifies a user supplied `UdevHandler` of any changes.
|
||||||
|
//!
|
||||||
|
//! Additionally this contains some utility functions related to scanning.
|
||||||
|
//!
|
||||||
|
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
|
||||||
|
//! backend.
|
||||||
|
|
||||||
|
use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||||
|
use backend::session::{Session, SessionObserver};
|
||||||
|
use nix::fcntl;
|
||||||
|
use nix::sys::stat::{dev_t, fstat};
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::io::{Error as IoError, Result as IoResult};
|
||||||
|
use std::mem::drop;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
|
||||||
|
use wayland_server::{EventLoopHandle, StateProxy, StateToken};
|
||||||
|
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
|
|
||||||
|
/// Graphical backend that monitors available drm devices.
|
||||||
|
///
|
||||||
|
/// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the
|
||||||
|
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
||||||
|
/// attached monitors.
|
||||||
|
pub struct UdevBackend<
|
||||||
|
B: Borrow<DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
S: Session + 'static,
|
||||||
|
T: UdevHandler<B, H> + 'static,
|
||||||
|
> {
|
||||||
|
devices: HashMap<
|
||||||
|
dev_t,
|
||||||
|
(
|
||||||
|
StateToken<DrmDevice<B>>,
|
||||||
|
FdEventSource<(StateToken<DrmDevice<B>>, H)>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
monitor: MonitorSocket,
|
||||||
|
session: S,
|
||||||
|
handler: T,
|
||||||
|
logger: ::slog::Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
S: Session + 'static,
|
||||||
|
T: UdevHandler<B, H> + 'static,
|
||||||
|
> UdevBackend<B, H, S, T> {
|
||||||
|
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
||||||
|
///
|
||||||
|
/// ## Arguments
|
||||||
|
/// `evlh` - An event loop to use for binding `DrmDevices`
|
||||||
|
/// `context` - An initialized udev context
|
||||||
|
/// `session` - A session used to open and close devices as they become available
|
||||||
|
/// `handler` - User-provided handler to respond to any detected changes
|
||||||
|
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
||||||
|
pub fn new<'a, L>(
|
||||||
|
mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L
|
||||||
|
) -> Result<StateToken<UdevBackend<B, H, S, T>>>
|
||||||
|
where
|
||||||
|
L: Into<Option<::slog::Logger>>,
|
||||||
|
{
|
||||||
|
let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev"));
|
||||||
|
let seat = session.seat();
|
||||||
|
let devices = all_gpus(context, seat)
|
||||||
|
.chain_err(|| ErrorKind::FailedToScan)?
|
||||||
|
.into_iter()
|
||||||
|
// Create devices
|
||||||
|
.flat_map(|path| {
|
||||||
|
match unsafe { DrmDevice::new_from_fd(
|
||||||
|
{
|
||||||
|
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, logger.clone()
|
||||||
|
) } {
|
||||||
|
// Call the handler, which might add it to the runloop
|
||||||
|
Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
|
||||||
|
// fstat them
|
||||||
|
Some(drm_handler) => match fstat(device.as_raw_fd()) {
|
||||||
|
Ok(stat) => {
|
||||||
|
let token = evlh.state().insert(device);
|
||||||
|
if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) {
|
||||||
|
Some((stat.st_rdev, (token, event_source)))
|
||||||
|
} else {
|
||||||
|
handler.device_removed(evlh.state(), &token);
|
||||||
|
let device = evlh.state().remove(token);
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
if let Err(err) = session.close(fd) {
|
||||||
|
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
||||||
|
};
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
// almost impossible to hit, but lets do it as good as possible
|
||||||
|
error!(logger, "Failed to get devnum of newly initialized device, dropping. Error: {:?}", err);
|
||||||
|
let token = evlh.state().insert(device);
|
||||||
|
handler.device_removed(evlh.state(), &token);
|
||||||
|
let device = evlh.state().remove(token);
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
if let Err(err) = session.close(fd) {
|
||||||
|
warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err);
|
||||||
|
};
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device); //drops master
|
||||||
|
if let Err(err) = session.close(fd) {
|
||||||
|
warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>>();
|
||||||
|
|
||||||
|
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||||
|
builder
|
||||||
|
.match_subsystem("drm")
|
||||||
|
.chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||||
|
let monitor = builder
|
||||||
|
.listen()
|
||||||
|
.chain_err(|| ErrorKind::FailedToInitMonitor)?;
|
||||||
|
|
||||||
|
Ok(evlh.state().insert(UdevBackend {
|
||||||
|
devices,
|
||||||
|
monitor,
|
||||||
|
session,
|
||||||
|
handler,
|
||||||
|
logger,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the udev backend and frees all remaining open devices.
|
||||||
|
///
|
||||||
|
/// Needs to be called after the `FdEventSource` was removed and the backend was removed from
|
||||||
|
/// the `EventLoop`'s `State`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// The given state might be passed to the registered `UdevHandler::device_removed` callback.
|
||||||
|
/// Make sure not to borrow any tokens twice.
|
||||||
|
pub fn close<'a, ST: Into<StateProxy<'a>>>(mut self, state: ST) {
|
||||||
|
let mut state = state.into();
|
||||||
|
for (_, (device, event_source)) in self.devices.drain() {
|
||||||
|
event_source.remove();
|
||||||
|
self.handler.device_removed(&mut state, &device);
|
||||||
|
let device = state.remove(device);
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
if let Err(err) = self.session.close(fd) {
|
||||||
|
warn!(
|
||||||
|
self.logger,
|
||||||
|
"Failed to close device. Error: {:?}. Ignoring", err
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
info!(self.logger, "All devices closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
B: Borrow<DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
S: Session + 'static,
|
||||||
|
T: UdevHandler<B, H> + 'static,
|
||||||
|
> SessionObserver for StateToken<UdevBackend<B, H, S, T>> {
|
||||||
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
|
state.with_value(self, |state, udev| {
|
||||||
|
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
||||||
|
device.pause(state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>) {
|
||||||
|
state.with_value(self, |state, udev| {
|
||||||
|
for &mut (ref mut device, _) in udev.devices.values_mut() {
|
||||||
|
device.activate(state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds a `UdevBackend` to a given `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
||||||
|
/// No runtime functionality can be provided without using this function.
|
||||||
|
pub fn udev_backend_bind<B, S, H, T>(
|
||||||
|
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>
|
||||||
|
) -> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
|
||||||
|
where
|
||||||
|
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
T: UdevHandler<B, H> + 'static,
|
||||||
|
S: Session + 'static,
|
||||||
|
{
|
||||||
|
let fd = evlh.state().get(&udev).monitor.as_raw_fd();
|
||||||
|
evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fd_event_source_implementation<B, S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>>
|
||||||
|
where
|
||||||
|
B: From<DrmBackend> + Borrow<DrmBackend> + 'static,
|
||||||
|
H: DrmHandler<B> + 'static,
|
||||||
|
T: UdevHandler<B, H> + 'static,
|
||||||
|
S: Session + 'static,
|
||||||
|
{
|
||||||
|
FdEventSourceImpl {
|
||||||
|
ready: |mut evlh, token, _, _| {
|
||||||
|
let events = evlh.state()
|
||||||
|
.get(token)
|
||||||
|
.monitor
|
||||||
|
.clone()
|
||||||
|
.collect::<Vec<Event>>();
|
||||||
|
for event in events {
|
||||||
|
match event.event_type() {
|
||||||
|
// New device
|
||||||
|
EventType::Add => {
|
||||||
|
info!(evlh.state().get(token).logger, "Device Added");
|
||||||
|
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
|
||||||
|
let mut device = {
|
||||||
|
match unsafe {
|
||||||
|
DrmDevice::new_from_fd(
|
||||||
|
{
|
||||||
|
let logger = evlh.state().get(token).logger.clone();
|
||||||
|
match evlh.state().get_mut(token).session.open(
|
||||||
|
path,
|
||||||
|
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
|
||||||
|
| fcntl::O_NONBLOCK,
|
||||||
|
) {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
evlh.state().get(token).logger.clone(),
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
Ok(dev) => dev,
|
||||||
|
Err(err) => {
|
||||||
|
warn!(
|
||||||
|
evlh.state().get(token).logger,
|
||||||
|
"Failed to initialize device {:?}. Error: {}. Skipping",
|
||||||
|
path,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match evlh.state().with_value(token, |state, udev| {
|
||||||
|
udev.handler.device_added(state, &mut device)
|
||||||
|
}) {
|
||||||
|
Some(drm_handler) => {
|
||||||
|
let dev_token = evlh.state().insert(device);
|
||||||
|
if let Ok(fd_event_source) =
|
||||||
|
drm_device_bind(&mut evlh, dev_token.clone(), drm_handler)
|
||||||
|
{
|
||||||
|
evlh.state()
|
||||||
|
.get_mut(token)
|
||||||
|
.devices
|
||||||
|
.insert(devnum, (dev_token, fd_event_source));
|
||||||
|
} else {
|
||||||
|
evlh.state().with_value(token, |state, udev| {
|
||||||
|
let mut state: StateProxy = state.into();
|
||||||
|
udev.handler.device_removed(&mut state, &dev_token);
|
||||||
|
let device = state.remove(dev_token);
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
if let Err(err) = udev.session.close(fd) {
|
||||||
|
warn!(
|
||||||
|
udev.logger,
|
||||||
|
"Failed to close dropped device. Error: {:?}. Ignoring",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
evlh.state().with_value(token, |_state, udev| {
|
||||||
|
if let Err(err) = udev.session.close(fd) {
|
||||||
|
warn!(
|
||||||
|
udev.logger,
|
||||||
|
"Failed to close unused device. Error: {:?}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Device removed
|
||||||
|
EventType::Remove => evlh.state().with_value(token, |state, udev| {
|
||||||
|
info!(udev.logger, "Device Remove");
|
||||||
|
if let Some(devnum) = event.devnum() {
|
||||||
|
if let Some((device, fd_event_source)) = udev.devices.remove(&devnum) {
|
||||||
|
fd_event_source.remove();
|
||||||
|
let mut state: StateProxy = state.into();
|
||||||
|
udev.handler.device_removed(&mut state, &device);
|
||||||
|
let device = state.remove(device);
|
||||||
|
let fd = device.as_raw_fd();
|
||||||
|
drop(device);
|
||||||
|
if let Err(err) = udev.session.close(fd) {
|
||||||
|
warn!(
|
||||||
|
udev.logger,
|
||||||
|
"Failed to close device {:?}. Error: {:?}. Ignoring",
|
||||||
|
event.sysname(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// New connector
|
||||||
|
EventType::Change => evlh.state().with_value(token, |state, udev| {
|
||||||
|
info!(udev.logger, "Device Changed");
|
||||||
|
if let Some(devnum) = event.devnum() {
|
||||||
|
info!(udev.logger, "Devnum: {:b}", devnum);
|
||||||
|
if let Some(&(ref device, _)) = udev.devices.get(&devnum) {
|
||||||
|
info!(udev.logger, "changed successful");
|
||||||
|
udev.handler.device_changed(state, device);
|
||||||
|
} else {
|
||||||
|
info!(udev.logger, "changed, but device not tracked by backend");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!(udev.logger, "changed, but no devnum");
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: |evlh, token, _, err| {
|
||||||
|
evlh.state()
|
||||||
|
.with_value(token, |state, udev| udev.handler.error(state, err))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
||||||
|
pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static>
|
||||||
|
{
|
||||||
|
/// Called on initialization for every known device and when a new device is detected.
|
||||||
|
///
|
||||||
|
/// Returning a `DrmHandler` will initialize the device, returning `None` will ignore the device.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
|
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>)
|
||||||
|
-> Option<H>;
|
||||||
|
/// Called when an open device is changed.
|
||||||
|
///
|
||||||
|
/// This usually indicates that some connectors did become available or were unplugged. The handler
|
||||||
|
/// should scan again for connected monitors and mode switch accordingly.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
||||||
|
/// Called when a device was removed.
|
||||||
|
///
|
||||||
|
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
||||||
|
/// this function returns, any open references/tokens to this device need to be released.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
||||||
|
/// Called when the udev context has encountered and error.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, error: IoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path of the primary gpu device if any
|
||||||
|
///
|
||||||
|
/// Might be used for filtering in `UdevHandler::device_added` or for manual `DrmDevice` initialization
|
||||||
|
pub fn primary_gpu<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Option<PathBuf>> {
|
||||||
|
let mut enumerator = Enumerator::new(context)?;
|
||||||
|
enumerator.match_subsystem("drm")?;
|
||||||
|
enumerator.match_sysname("card[0-9]*")?;
|
||||||
|
|
||||||
|
let mut result = None;
|
||||||
|
for device in enumerator.scan_devices()? {
|
||||||
|
if device
|
||||||
|
.property_value("ID_SEAT")
|
||||||
|
.map(|x| x.to_os_string())
|
||||||
|
.unwrap_or(OsString::from("seat0")) == *seat.as_ref()
|
||||||
|
{
|
||||||
|
if let Some(pci) = device.parent_with_subsystem(Path::new("pci"))? {
|
||||||
|
if let Some(id) = pci.attribute_value("boot_vga") {
|
||||||
|
if id == "1" {
|
||||||
|
result = Some(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if result.is_none() {
|
||||||
|
result = Some(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(result.and_then(|device| device.devnode().map(PathBuf::from)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the paths of all available gpu devices
|
||||||
|
///
|
||||||
|
/// Might be used for manual `DrmDevice` initialization
|
||||||
|
pub fn all_gpus<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Vec<PathBuf>> {
|
||||||
|
let mut enumerator = Enumerator::new(context)?;
|
||||||
|
enumerator.match_subsystem("drm")?;
|
||||||
|
enumerator.match_sysname("card[0-9]*")?;
|
||||||
|
Ok(enumerator
|
||||||
|
.scan_devices()?
|
||||||
|
.filter(|device| {
|
||||||
|
device
|
||||||
|
.property_value("ID_SEAT")
|
||||||
|
.map(|x| x.to_os_string())
|
||||||
|
.unwrap_or(OsString::from("seat0")) == *seat.as_ref()
|
||||||
|
})
|
||||||
|
.flat_map(|device| device.devnode().map(PathBuf::from))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
#[doc = "Failed to scan for devices"]
|
||||||
|
FailedToScan {
|
||||||
|
description("Failed to scan for devices"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to initialize udev monitor"]
|
||||||
|
FailedToInitMonitor {
|
||||||
|
description("Failed to initialize udev monitor"),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = "Failed to identify devices"]
|
||||||
|
FailedToIdentifyDevices {
|
||||||
|
description("Failed to identify devices"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
//! Implementation of backend traits for types provided by `winit`
|
//! Implementation of backend traits for types provided by `winit`
|
||||||
|
|
||||||
use backend::{SeatInternal, TouchSlotInternal};
|
|
||||||
use backend::graphics::GraphicsBackend;
|
use backend::graphics::GraphicsBackend;
|
||||||
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
|
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat,
|
||||||
PixelFormatRequirements, SwapBuffersError};
|
PixelFormatRequirements, SwapBuffersError};
|
||||||
|
@ -100,8 +99,9 @@ where
|
||||||
/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend`
|
/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend`
|
||||||
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
|
/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding
|
||||||
/// `WinitInputBackend`, which implements the `InputBackend` trait
|
/// `WinitInputBackend`, which implements the `InputBackend` trait
|
||||||
pub fn init_from_builder<L>(builder: WindowBuilder, logger: L)
|
pub fn init_from_builder<L>(
|
||||||
-> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
builder: WindowBuilder, logger: L
|
||||||
|
) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -121,8 +121,9 @@ where
|
||||||
/// graphics backend trait, from a given `WindowBuilder` struct, as well as given
|
/// graphics backend trait, from a given `WindowBuilder` struct, as well as given
|
||||||
/// `GlAttributes` for further customization of the rendering pipeline and a
|
/// `GlAttributes` for further customization of the rendering pipeline and a
|
||||||
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
|
/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait.
|
||||||
pub fn init_from_builder_with_gl_attr<L>(builder: WindowBuilder, attributes: GlAttributes, logger: L)
|
pub fn init_from_builder_with_gl_attr<L>(
|
||||||
-> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
builder: WindowBuilder, attributes: GlAttributes, logger: L
|
||||||
|
) -> Result<(WinitGraphicsBackend, WinitInputBackend)>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -191,11 +192,12 @@ impl GraphicsBackend for WinitGraphicsBackend {
|
||||||
self.window.head().set_cursor_position(x as i32, y as i32)
|
self.window.head().set_cursor_position(x as i32, y as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_representation(&self, cursor: Self::CursorFormat, _hotspot: (u32, u32))
|
fn set_cursor_representation(
|
||||||
-> ::std::result::Result<(), ()> {
|
&self, cursor: &Self::CursorFormat, _hotspot: (u32, u32)
|
||||||
|
) -> ::std::result::Result<(), ()> {
|
||||||
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
||||||
debug!(self.logger, "Changing cursor representation");
|
debug!(self.logger, "Changing cursor representation");
|
||||||
self.window.head().set_cursor(cursor);
|
self.window.head().set_cursor(*cursor);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +231,8 @@ impl EGLGraphicsBackend for WinitGraphicsBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current(&self) -> bool {
|
fn is_current(&self) -> bool {
|
||||||
self.window.rent(|egl| egl.head().is_current())
|
self.window
|
||||||
|
.rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current()))
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
|
||||||
|
@ -372,10 +375,10 @@ impl PointerAxisEvent for WinitMouseWheelEvent {
|
||||||
|
|
||||||
fn amount(&self) -> f64 {
|
fn amount(&self) -> f64 {
|
||||||
match (self.axis, self.delta) {
|
match (self.axis, self.delta) {
|
||||||
(Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) |
|
(Axis::Horizontal, MouseScrollDelta::LineDelta(x, _))
|
||||||
(Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64,
|
| (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64,
|
||||||
(Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) |
|
(Axis::Vertical, MouseScrollDelta::LineDelta(_, y))
|
||||||
(Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64,
|
| (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -625,8 +628,10 @@ impl InputBackend for WinitInputBackend {
|
||||||
trace!(logger, "Resizing window to {:?}", (x, y));
|
trace!(logger, "Resizing window to {:?}", (x, y));
|
||||||
window.head().set_inner_size(x, y);
|
window.head().set_inner_size(x, y);
|
||||||
window.rent(|egl| {
|
window.rent(|egl| {
|
||||||
egl.rent(|surface| if let Some(wegl_surface) = (**surface).as_ref() {
|
egl.rent(|surface| {
|
||||||
|
if let Some(wegl_surface) = (**surface).as_ref() {
|
||||||
wegl_surface.resize(x as i32, y as i32, 0, 0)
|
wegl_surface.resize(x as i32, y as i32, 0, 0)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
extern crate image;
|
extern crate image;
|
||||||
|
#[macro_use]
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rental;
|
extern crate rental;
|
||||||
|
@ -22,6 +23,14 @@ extern crate drm;
|
||||||
extern crate gbm;
|
extern crate gbm;
|
||||||
#[cfg(feature = "backend_libinput")]
|
#[cfg(feature = "backend_libinput")]
|
||||||
extern crate input;
|
extern crate input;
|
||||||
|
#[cfg(feature = "udev")]
|
||||||
|
extern crate udev;
|
||||||
|
/*
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
extern crate dbus;
|
||||||
|
#[cfg(feature = "backend_session_logind")]
|
||||||
|
extern crate systemd;
|
||||||
|
*/
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
extern crate wayland_client;
|
extern crate wayland_client;
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
|
|
|
@ -11,9 +11,10 @@ use wayland_server::protocol::{wl_compositor, wl_region, wl_subcompositor, wl_su
|
||||||
* wl_compositor
|
* wl_compositor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn compositor_bind<U, R, ID>(evlh: &mut EventLoopHandle, idata: &mut SurfaceIData<U, R, ID>,
|
pub(crate) fn compositor_bind<U, R, ID>(
|
||||||
_: &Client, compositor: wl_compositor::WlCompositor)
|
evlh: &mut EventLoopHandle, idata: &mut SurfaceIData<U, R, ID>, _: &Client,
|
||||||
where
|
compositor: wl_compositor::WlCompositor,
|
||||||
|
) where
|
||||||
U: Default + 'static,
|
U: Default + 'static,
|
||||||
R: Default + 'static,
|
R: Default + 'static,
|
||||||
ID: 'static,
|
ID: 'static,
|
||||||
|
@ -65,8 +66,9 @@ pub struct SurfaceIData<U, R, ID> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U, R, ID> SurfaceIData<U, R, ID> {
|
impl<U, R, ID> SurfaceIData<U, R, ID> {
|
||||||
pub(crate) fn make(log: ::slog::Logger, implem: SurfaceUserImplementation<U, R, ID>, idata: ID)
|
pub(crate) fn make(
|
||||||
-> SurfaceIData<U, R, ID> {
|
log: ::slog::Logger, implem: SurfaceUserImplementation<U, R, ID>, idata: ID
|
||||||
|
) -> SurfaceIData<U, R, ID> {
|
||||||
SurfaceIData {
|
SurfaceIData {
|
||||||
log: log,
|
log: log,
|
||||||
implem: implem,
|
implem: implem,
|
||||||
|
@ -86,9 +88,7 @@ impl<U, R, ID> Clone for SurfaceIData<U, R, ID> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
|
pub(crate) fn surface_implementation<U: 'static, R: 'static, ID: 'static>(
|
||||||
)
|
) -> wl_surface::Implementation<SurfaceIData<U, R, ID>> {
|
||||||
-> wl_surface::Implementation<SurfaceIData<U, R, ID>>
|
|
||||||
{
|
|
||||||
wl_surface::Implementation {
|
wl_surface::Implementation {
|
||||||
attach: |_, _, _, surface, buffer, x, y| unsafe {
|
attach: |_, _, _, surface, buffer, x, y| unsafe {
|
||||||
SurfaceData::<U, R>::with_data(surface, |d| {
|
SurfaceData::<U, R>::with_data(surface, |d| {
|
||||||
|
@ -199,9 +199,9 @@ fn destroy_region(region: &wl_region::WlRegion) {
|
||||||
* wl_subcompositor
|
* wl_subcompositor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub(crate) fn subcompositor_bind<U, R>(evlh: &mut EventLoopHandle, _: &mut (), _: &Client,
|
pub(crate) fn subcompositor_bind<U, R>(
|
||||||
subcompositor: wl_subcompositor::WlSubcompositor)
|
evlh: &mut EventLoopHandle, _: &mut (), _: &Client, subcompositor: wl_subcompositor::WlSubcompositor
|
||||||
where
|
) where
|
||||||
R: RoleType + Role<SubsurfaceRole> + 'static,
|
R: RoleType + Role<SubsurfaceRole> + 'static,
|
||||||
U: 'static,
|
U: 'static,
|
||||||
{
|
{
|
||||||
|
|
|
@ -314,11 +314,11 @@ where
|
||||||
///
|
///
|
||||||
/// If the surface not managed by the CompositorGlobal that provided this token, this
|
/// If the surface not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// will panic (having more than one compositor is not supported).
|
||||||
pub fn with_surface_tree_upward<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F)
|
pub fn with_surface_tree_upward<F, T>(
|
||||||
-> Result<(), ()>
|
&self, surface: &wl_surface::WlSurface, initial: T, f: F
|
||||||
|
) -> Result<(), ()>
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
|
||||||
-> TraversalAction<T>,
|
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
resource_is_registered(
|
resource_is_registered(
|
||||||
|
@ -340,11 +340,11 @@ where
|
||||||
/// supposed to be drawn: top-most first.
|
/// supposed to be drawn: top-most first.
|
||||||
///
|
///
|
||||||
/// Behavior is the same as `with_surface_tree_upward`.
|
/// Behavior is the same as `with_surface_tree_upward`.
|
||||||
pub fn with_surface_tree_downward<F, T>(&self, surface: &wl_surface::WlSurface, initial: T, f: F)
|
pub fn with_surface_tree_downward<F, T>(
|
||||||
-> Result<(), ()>
|
&self, surface: &wl_surface::WlSurface, initial: T, f: F
|
||||||
|
) -> Result<(), ()>
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
|
||||||
-> TraversalAction<T>,
|
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
resource_is_registered(
|
resource_is_registered(
|
||||||
|
@ -426,7 +426,6 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
|
||||||
unsafe { SurfaceData::<U, R>::has_role::<RoleData>(surface) }
|
unsafe { SurfaceData::<U, R>::has_role::<RoleData>(surface) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Register that this surface has given role with default data
|
/// Register that this surface has given role with default data
|
||||||
///
|
///
|
||||||
/// Fails if the surface already has a role.
|
/// Fails if the surface already has a role.
|
||||||
|
@ -454,8 +453,9 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
|
||||||
///
|
///
|
||||||
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// will panic (having more than one compositor is not supported).
|
||||||
pub fn give_role_with<RoleData>(&self, surface: &wl_surface::WlSurface, data: RoleData)
|
pub fn give_role_with<RoleData>(
|
||||||
-> Result<(), RoleData>
|
&self, surface: &wl_surface::WlSurface, data: RoleData
|
||||||
|
) -> Result<(), RoleData>
|
||||||
where
|
where
|
||||||
R: Role<RoleData>,
|
R: Role<RoleData>,
|
||||||
{
|
{
|
||||||
|
@ -475,8 +475,9 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
|
||||||
///
|
///
|
||||||
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
/// If the surface is not managed by the CompositorGlobal that provided this token, this
|
||||||
/// will panic (having more than one compositor is not supported).
|
/// will panic (having more than one compositor is not supported).
|
||||||
pub fn with_role_data<RoleData, F, T>(&self, surface: &wl_surface::WlSurface, f: F)
|
pub fn with_role_data<RoleData, F, T>(
|
||||||
-> Result<T, WrongRole>
|
&self, surface: &wl_surface::WlSurface, f: F
|
||||||
|
) -> Result<T, WrongRole>
|
||||||
where
|
where
|
||||||
R: Role<RoleData>,
|
R: Role<RoleData>,
|
||||||
F: FnOnce(&mut RoleData) -> T,
|
F: FnOnce(&mut RoleData) -> T,
|
||||||
|
@ -533,8 +534,8 @@ impl<U: 'static, R: RoleType + 'static, ID: 'static> CompositorToken<U, R, ID> {
|
||||||
/// It also returns the two global handles, in case you whish to remove these
|
/// It also returns the two global handles, in case you whish to remove these
|
||||||
/// globals from the event loop in the future.
|
/// globals from the event loop in the future.
|
||||||
pub fn compositor_init<U, R, ID, L>(
|
pub fn compositor_init<U, R, ID, L>(
|
||||||
evl: &mut EventLoop, implem: SurfaceUserImplementation<U, R, ID>, idata: ID, logger: L)
|
evl: &mut EventLoop, implem: SurfaceUserImplementation<U, R, ID>, idata: ID, logger: L
|
||||||
-> (
|
) -> (
|
||||||
CompositorToken<U, R, ID>,
|
CompositorToken<U, R, ID>,
|
||||||
Global<wl_compositor::WlCompositor, self::handlers::SurfaceIData<U, R, ID>>,
|
Global<wl_compositor::WlCompositor, self::handlers::SurfaceIData<U, R, ID>>,
|
||||||
Global<wl_subcompositor::WlSubcompositor, ()>,
|
Global<wl_subcompositor::WlSubcompositor, ()>,
|
||||||
|
|
|
@ -132,8 +132,9 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
|
||||||
/// Register that this surface has a role with given data
|
/// Register that this surface has a role with given data
|
||||||
///
|
///
|
||||||
/// Fails if it already has one and returns the data
|
/// Fails if it already has one and returns the data
|
||||||
pub unsafe fn give_role_with<RoleData>(surface: &wl_surface::WlSurface, data: RoleData)
|
pub unsafe fn give_role_with<RoleData>(
|
||||||
-> Result<(), RoleData>
|
surface: &wl_surface::WlSurface, data: RoleData
|
||||||
|
) -> Result<(), RoleData>
|
||||||
where
|
where
|
||||||
R: Role<RoleData>,
|
R: Role<RoleData>,
|
||||||
{
|
{
|
||||||
|
@ -158,8 +159,9 @@ impl<U: 'static, R: RoleType + 'static> SurfaceData<U, R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access to the role data
|
/// Access to the role data
|
||||||
pub unsafe fn with_role_data<RoleData, F, T>(surface: &wl_surface::WlSurface, f: F)
|
pub unsafe fn with_role_data<RoleData, F, T>(
|
||||||
-> Result<T, WrongRole>
|
surface: &wl_surface::WlSurface, f: F
|
||||||
|
) -> Result<T, WrongRole>
|
||||||
where
|
where
|
||||||
R: Role<RoleData>,
|
R: Role<RoleData>,
|
||||||
F: FnOnce(&mut RoleData) -> T,
|
F: FnOnce(&mut RoleData) -> T,
|
||||||
|
@ -177,8 +179,9 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
|
||||||
///
|
///
|
||||||
/// if this surface already has a role, does nothing and fails, otherwise
|
/// if this surface already has a role, does nothing and fails, otherwise
|
||||||
/// its role is now to be a subsurface
|
/// its role is now to be a subsurface
|
||||||
pub unsafe fn set_parent(child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface)
|
pub unsafe fn set_parent(
|
||||||
-> Result<(), ()> {
|
child: &wl_surface::WlSurface, parent: &wl_surface::WlSurface
|
||||||
|
) -> Result<(), ()> {
|
||||||
debug_assert!(child.status() == Liveness::Alive);
|
debug_assert!(child.status() == Liveness::Alive);
|
||||||
debug_assert!(parent.status() == Liveness::Alive);
|
debug_assert!(parent.status() == Liveness::Alive);
|
||||||
|
|
||||||
|
@ -246,9 +249,9 @@ impl<U: 'static, R: RoleType + Role<SubsurfaceRole> + 'static> SurfaceData<U, R>
|
||||||
/// Reorders a surface relative to one of its sibling
|
/// Reorders a surface relative to one of its sibling
|
||||||
///
|
///
|
||||||
/// Fails if `relative_to` is not a sibling or parent of `surface`.
|
/// Fails if `relative_to` is not a sibling or parent of `surface`.
|
||||||
pub unsafe fn reorder(surface: &wl_surface::WlSurface, to: Location,
|
pub unsafe fn reorder(
|
||||||
relative_to: &wl_surface::WlSurface)
|
surface: &wl_surface::WlSurface, to: Location, relative_to: &wl_surface::WlSurface
|
||||||
-> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
let parent = {
|
let parent = {
|
||||||
let data_mutex = Self::get_data(surface);
|
let data_mutex = Self::get_data(surface);
|
||||||
let data_guard = data_mutex.lock().unwrap();
|
let data_guard = data_mutex.lock().unwrap();
|
||||||
|
@ -316,17 +319,15 @@ impl<U: 'static, R: 'static> SurfaceData<U, R> {
|
||||||
/// false will cause an early-stopping.
|
/// false will cause an early-stopping.
|
||||||
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool)
|
pub unsafe fn map_tree<F, T>(root: &wl_surface::WlSurface, initial: T, mut f: F, reverse: bool)
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
|
||||||
-> TraversalAction<T>,
|
|
||||||
{
|
{
|
||||||
// helper function for recursion
|
// helper function for recursion
|
||||||
unsafe fn map<U: 'static, R: 'static, F, T>(surface: &wl_surface::WlSurface,
|
unsafe fn map<U: 'static, R: 'static, F, T>(
|
||||||
root: &wl_surface::WlSurface, initial: &T, f: &mut F,
|
surface: &wl_surface::WlSurface, root: &wl_surface::WlSurface, initial: &T, f: &mut F,
|
||||||
reverse: bool)
|
reverse: bool,
|
||||||
-> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T)
|
F: FnMut(&wl_surface::WlSurface, &mut SurfaceAttributes<U>, &mut R, &T) -> TraversalAction<T>,
|
||||||
-> TraversalAction<T>,
|
|
||||||
{
|
{
|
||||||
// stop if we met the root, so to not deadlock/inifinte loop
|
// stop if we met the root, so to not deadlock/inifinte loop
|
||||||
if surface.equals(root) {
|
if surface.equals(root) {
|
||||||
|
|
|
@ -113,8 +113,8 @@ impl Output {
|
||||||
/// returns the state token allowing you to access it, as well as the global handle,
|
/// returns the state token allowing you to access it, as well as the global handle,
|
||||||
/// in case you whish to remove this global in the future.
|
/// in case you whish to remove this global in the future.
|
||||||
pub fn new<L>(
|
pub fn new<L>(
|
||||||
evl: &mut EventLoop, name: String, physical: PhysicalProperties, logger: L)
|
evl: &mut EventLoop, name: String, physical: PhysicalProperties, logger: L
|
||||||
-> (
|
) -> (
|
||||||
StateToken<Output>,
|
StateToken<Output>,
|
||||||
Global<wl_output::WlOutput, StateToken<Output>>,
|
Global<wl_output::WlOutput, StateToken<Output>>,
|
||||||
)
|
)
|
||||||
|
@ -229,8 +229,10 @@ impl Output {
|
||||||
/// internal list.
|
/// internal list.
|
||||||
///
|
///
|
||||||
/// By default, transform status is `Normal`, and scale is `1`.
|
/// By default, transform status is `Normal`, and scale is `1`.
|
||||||
pub fn change_current_state(&mut self, new_mode: Option<Mode>,
|
pub fn change_current_state(
|
||||||
new_transform: Option<wl_output::Transform>, new_scale: Option<i32>) {
|
&mut self, new_mode: Option<Mode>, new_transform: Option<wl_output::Transform>,
|
||||||
|
new_scale: Option<i32>,
|
||||||
|
) {
|
||||||
if let Some(mode) = new_mode {
|
if let Some(mode) = new_mode {
|
||||||
if self.modes.iter().find(|&m| *m == mode).is_none() {
|
if self.modes.iter().find(|&m| *m == mode).is_none() {
|
||||||
self.modes.push(mode);
|
self.modes.push(mode);
|
||||||
|
@ -283,8 +285,9 @@ impl Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Output>, _: &Client,
|
fn output_bind(
|
||||||
global: wl_output::WlOutput) {
|
evlh: &mut EventLoopHandle, token: &mut StateToken<Output>, _: &Client, global: wl_output::WlOutput
|
||||||
|
) {
|
||||||
evlh.register(&global, output_implementation(), token.clone(), None);
|
evlh.register(&global, output_implementation(), token.clone(), None);
|
||||||
evlh.state().get_mut(token).new_global(global);
|
evlh.state().get_mut(token).new_global(global);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,9 +66,10 @@ struct KbdInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbdInternal {
|
impl KbdInternal {
|
||||||
fn new(rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>,
|
fn new(
|
||||||
repeat_rate: i32, repeat_delay: i32)
|
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_rate: i32,
|
||||||
-> Result<KbdInternal, ()> {
|
repeat_delay: i32,
|
||||||
|
) -> Result<KbdInternal, ()> {
|
||||||
// we create a new contex for each keyboard because libxkbcommon is actually NOT threadsafe
|
// 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
|
// so confining it inside the KbdInternal allows us to use Rusts mutability rules to make
|
||||||
// sure nothing goes wrong.
|
// sure nothing goes wrong.
|
||||||
|
@ -168,10 +169,10 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a keyboard handler from a set of RMLVO rules
|
/// Create a keyboard handler from a set of RMLVO rules
|
||||||
pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, variant: &str,
|
pub(crate) fn create_keyboard_handler(
|
||||||
options: Option<String>, repeat_delay: i32, repeat_rate: i32,
|
rules: &str, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32,
|
||||||
logger: &::slog::Logger)
|
repeat_rate: i32, logger: &::slog::Logger,
|
||||||
-> Result<KeyboardHandle, Error> {
|
) -> Result<KeyboardHandle, Error> {
|
||||||
let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
|
let log = logger.new(o!("smithay_module" => "xkbcommon_handler"));
|
||||||
info!(log, "Initializing a xkbcommon handler with keymap query";
|
info!(log, "Initializing a xkbcommon handler with keymap query";
|
||||||
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
"rules" => rules, "model" => model, "layout" => layout, "variant" => variant,
|
||||||
|
@ -192,7 +193,6 @@ pub(crate) fn create_keyboard_handler(rules: &str, model: &str, layout: &str, va
|
||||||
|
|
||||||
info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next());
|
info!(log, "Loaded Keymap"; "name" => internal.keymap.layouts().next());
|
||||||
|
|
||||||
|
|
||||||
// prepare a tempfile with the keymap, to send it to clients
|
// prepare a tempfile with the keymap, to send it to clients
|
||||||
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
let mut keymap_file = tempfile().map_err(Error::IoError)?;
|
||||||
let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
|
let keymap_data = internal.keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
|
||||||
|
|
|
@ -87,8 +87,9 @@ impl Seat {
|
||||||
/// You are provided with the state token to retrieve it (allowing
|
/// You are provided with the state token to retrieve it (allowing
|
||||||
/// you to add or remove capabilities from it), and the global handle,
|
/// you to add or remove capabilities from it), and the global handle,
|
||||||
/// in case you want to remove it.
|
/// in case you want to remove it.
|
||||||
pub fn new<L>(evl: &mut EventLoop, name: String, logger: L)
|
pub fn new<L>(
|
||||||
-> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
|
evl: &mut EventLoop, name: String, logger: L
|
||||||
|
) -> (StateToken<Seat>, Global<wl_seat::WlSeat, StateToken<Seat>>)
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
@ -158,9 +159,10 @@ impl Seat {
|
||||||
/// Calling this method on a seat that already has a keyboard capability
|
/// Calling this method on a seat that already has a keyboard capability
|
||||||
/// will overwrite it, and will be seen by the clients as if the
|
/// will overwrite it, and will be seen by the clients as if the
|
||||||
/// keyboard was unplugged and a new one was plugged.
|
/// keyboard was unplugged and a new one was plugged.
|
||||||
pub fn add_keyboard(&mut self, model: &str, layout: &str, variant: &str, options: Option<String>,
|
pub fn add_keyboard(
|
||||||
repeat_delay: i32, repeat_rate: i32)
|
&mut self, model: &str, layout: &str, variant: &str, options: Option<String>, repeat_delay: i32,
|
||||||
-> Result<KeyboardHandle, KeyboardError> {
|
repeat_rate: i32,
|
||||||
|
) -> Result<KeyboardHandle, KeyboardError> {
|
||||||
let keyboard = self::keyboard::create_keyboard_handler(
|
let keyboard = self::keyboard::create_keyboard_handler(
|
||||||
"evdev", // we need this one
|
"evdev", // we need this one
|
||||||
model,
|
model,
|
||||||
|
@ -229,8 +231,9 @@ impl Seat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seat_global_bind(evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client,
|
fn seat_global_bind(
|
||||||
seat: wl_seat::WlSeat) {
|
evlh: &mut EventLoopHandle, token: &mut StateToken<Seat>, _: &Client, seat: wl_seat::WlSeat
|
||||||
|
) {
|
||||||
evlh.register(&seat, seat_implementation(), token.clone(), None);
|
evlh.register(&seat, seat_implementation(), token.clone(), None);
|
||||||
let seat_mgr = evlh.state().get_mut(token);
|
let seat_mgr = evlh.state().get_mut(token);
|
||||||
seat.name(seat_mgr.name.clone());
|
seat.name(seat_mgr.name.clone());
|
||||||
|
|
|
@ -302,8 +302,8 @@ impl<U, R, CID, SID, SD> Clone for ShellSurfaceIData<U, R, CID, SID, SD> {
|
||||||
/// globals from the event loop in the future.
|
/// globals from the event loop in the future.
|
||||||
pub fn shell_init<U, R, CID, SID, SD, L>(
|
pub fn shell_init<U, R, CID, SID, SD, L>(
|
||||||
evl: &mut EventLoop, token: CompositorToken<U, R, CID>,
|
evl: &mut EventLoop, token: CompositorToken<U, R, CID>,
|
||||||
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>, idata: SID, logger: L)
|
implementation: ShellSurfaceUserImplementation<U, R, CID, SID, SD>, idata: SID, logger: L,
|
||||||
-> (
|
) -> (
|
||||||
StateToken<ShellState<U, R, CID, SD>>,
|
StateToken<ShellState<U, R, CID, SD>>,
|
||||||
Global<wl_shell::WlShell, ShellSurfaceIData<U, R, CID, SID, SD>>,
|
Global<wl_shell::WlShell, ShellSurfaceIData<U, R, CID, SID, SD>>,
|
||||||
Global<zxdg_shell_v6::ZxdgShellV6, ShellSurfaceIData<U, R, CID, SID, SD>>,
|
Global<zxdg_shell_v6::ZxdgShellV6, ShellSurfaceIData<U, R, CID, SID, SD>>,
|
||||||
|
@ -449,7 +449,8 @@ impl<SD> ShellClient<SD> {
|
||||||
}
|
}
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ShellClientKind::Wl(ref shell) => {
|
ShellClientKind::Wl(ref shell) => {
|
||||||
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
let mutex =
|
||||||
|
unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
if guard.0.pending_ping == 0 {
|
if guard.0.pending_ping == 0 {
|
||||||
return Err(());
|
return Err(());
|
||||||
|
@ -486,7 +487,8 @@ impl<SD> ShellClient<SD> {
|
||||||
}
|
}
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ShellClientKind::Wl(ref shell) => {
|
ShellClientKind::Wl(ref shell) => {
|
||||||
let mutex = unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
let mutex =
|
||||||
|
unsafe { &*(shell.get_user_data() as *mut self::wl_handlers::ShellUserData<SD>) };
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
Ok(f(&mut guard.0.data))
|
Ok(f(&mut guard.0.data))
|
||||||
}
|
}
|
||||||
|
@ -844,11 +846,9 @@ pub struct ShellSurfaceUserImplementation<U, R, CID, SID, SD> {
|
||||||
///
|
///
|
||||||
/// You need to return a `ToplevelConfigure` from this function, which will be sent
|
/// You need to return a `ToplevelConfigure` from this function, which will be sent
|
||||||
/// to the client to configure this surface
|
/// to the client to configure this surface
|
||||||
pub new_toplevel: fn(
|
pub new_toplevel:
|
||||||
evlh: &mut EventLoopHandle,
|
fn(evlh: &mut EventLoopHandle, idata: &mut SID, surface: ToplevelSurface<U, R, CID, SD>)
|
||||||
idata: &mut SID,
|
-> ToplevelConfigure,
|
||||||
surface: ToplevelSurface<U, R, CID, SD>,
|
|
||||||
) -> ToplevelConfigure,
|
|
||||||
/// A new popup surface was created
|
/// A new popup surface was created
|
||||||
///
|
///
|
||||||
/// You need to return a `PopupConfigure` from this function, which will be sent
|
/// You need to return a `PopupConfigure` from this function, which will be sent
|
||||||
|
|
|
@ -10,10 +10,10 @@ use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_positioner_v6 as x
|
||||||
use wayland_server::{Client, EventLoopHandle, Resource};
|
use wayland_server::{Client, EventLoopHandle, Resource};
|
||||||
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
|
use wayland_server::protocol::{wl_output, wl_shell, wl_shell_surface, wl_surface};
|
||||||
|
|
||||||
pub(crate) fn wl_shell_bind<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
|
pub(crate) fn wl_shell_bind<U, R, CID, SID, SD>(
|
||||||
idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>,
|
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
|
||||||
_: &Client, shell: wl_shell::WlShell)
|
shell: wl_shell::WlShell,
|
||||||
where
|
) where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -56,8 +56,7 @@ pub fn make_shell_client<SD>(resource: &wl_shell::WlShell) -> ShellClient<SD> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_implementation<U, R, CID, SID, SD>(
|
fn shell_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> wl_shell::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> wl_shell::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
@ -122,9 +121,9 @@ fn destroy_shell_surface(shell_surface: &wl_shell_surface::WlShellSurface) {
|
||||||
::std::mem::drop(surface);
|
::std::mem::drop(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
|
fn make_toplevel_handle<U, R, H, SD>(
|
||||||
resource: &wl_shell_surface::WlShellSurface)
|
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
|
||||||
-> super::ToplevelSurface<U, R, H, SD> {
|
) -> super::ToplevelSurface<U, R, H, SD> {
|
||||||
let ptr = resource.get_user_data();
|
let ptr = resource.get_user_data();
|
||||||
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
super::ToplevelSurface {
|
super::ToplevelSurface {
|
||||||
|
@ -135,9 +134,9 @@ fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_popup_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
|
fn make_popup_handle<U, R, H, SD>(
|
||||||
resource: &wl_shell_surface::WlShellSurface)
|
token: CompositorToken<U, R, H>, resource: &wl_shell_surface::WlShellSurface
|
||||||
-> super::PopupSurface<U, R, H, SD> {
|
) -> super::PopupSurface<U, R, H, SD> {
|
||||||
let ptr = resource.get_user_data();
|
let ptr = resource.get_user_data();
|
||||||
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
let &(ref wl_surface, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
super::PopupSurface {
|
super::PopupSurface {
|
||||||
|
@ -158,12 +157,11 @@ pub fn send_popup_configure(resource: &wl_shell_surface::WlShellSurface, configu
|
||||||
resource.configure(wl_shell_surface::Resize::empty(), w, h);
|
resource.configure(wl_shell_surface::Resize::empty(), w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wl_handle_display_state_change<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
|
fn wl_handle_display_state_change<U, R, CID, SID, SD>(
|
||||||
idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
||||||
shell_surface: &wl_shell_surface::WlShellSurface,
|
shell_surface: &wl_shell_surface::WlShellSurface, maximized: Option<bool>, minimized: Option<bool>,
|
||||||
maximized: Option<bool>, minimized: Option<bool>,
|
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
|
||||||
fullscreen: Option<bool>,
|
) {
|
||||||
output: Option<&wl_output::WlOutput>) {
|
|
||||||
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
|
let handle = make_toplevel_handle(idata.compositor_token, shell_surface);
|
||||||
// handler callback
|
// handler callback
|
||||||
let mut user_idata = idata.idata.borrow_mut();
|
let mut user_idata = idata.idata.borrow_mut();
|
||||||
|
@ -181,10 +179,10 @@ fn wl_handle_display_state_change<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle
|
||||||
shell_surface.configure(wl_shell_surface::Resize::None, w, h);
|
shell_surface.configure(wl_shell_surface::Resize::None, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wl_set_parent<U, R, CID, SID, SD>(idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
fn wl_set_parent<U, R, CID, SID, SD>(
|
||||||
shell_surface: &wl_shell_surface::WlShellSurface,
|
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, shell_surface: &wl_shell_surface::WlShellSurface,
|
||||||
parent: Option<wl_surface::WlSurface>)
|
parent: Option<wl_surface::WlSurface>,
|
||||||
where
|
) where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -204,10 +202,10 @@ where
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wl_ensure_toplevel<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
|
fn wl_ensure_toplevel<U, R, CID, SID, SD>(
|
||||||
idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
||||||
shell_surface: &wl_shell_surface::WlShellSurface)
|
shell_surface: &wl_shell_surface::WlShellSurface,
|
||||||
where
|
) where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -263,8 +261,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_surface_implementation<U, R, CID, SID, SD>(
|
fn shell_surface_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> wl_shell_surface::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> wl_shell_surface::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
@ -274,7 +271,8 @@ where
|
||||||
{
|
{
|
||||||
wl_shell_surface::Implementation {
|
wl_shell_surface::Implementation {
|
||||||
pong: |evlh, idata, _, shell_surface, serial| {
|
pong: |evlh, idata, _, shell_surface, serial| {
|
||||||
let &(_, ref shell) = unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
|
let &(_, ref shell) =
|
||||||
|
unsafe { &*(shell_surface.get_user_data() as *mut ShellSurfaceUserData) };
|
||||||
let valid = {
|
let valid = {
|
||||||
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
|
let mutex = unsafe { &*(shell.get_user_data() as *mut ShellUserData<SD>) };
|
||||||
let mut guard = mutex.lock().unwrap();
|
let mut guard = mutex.lock().unwrap();
|
||||||
|
|
|
@ -10,10 +10,10 @@ use wayland_protocols::unstable::xdg_shell::v6::server::{zxdg_popup_v6, zxdg_pos
|
||||||
use wayland_server::{Client, EventLoopHandle, Resource};
|
use wayland_server::{Client, EventLoopHandle, Resource};
|
||||||
use wayland_server::protocol::{wl_output, wl_surface};
|
use wayland_server::protocol::{wl_output, wl_surface};
|
||||||
|
|
||||||
pub(crate) fn xdg_shell_bind<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
|
pub(crate) fn xdg_shell_bind<U, R, CID, SID, SD>(
|
||||||
idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>,
|
evlh: &mut EventLoopHandle, idata: &mut ShellSurfaceIData<U, R, CID, SID, SD>, _: &Client,
|
||||||
_: &Client, shell: zxdg_shell_v6::ZxdgShellV6)
|
shell: zxdg_shell_v6::ZxdgShellV6,
|
||||||
where
|
) where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -53,8 +53,7 @@ pub(crate) fn make_shell_client<SD>(resource: &zxdg_shell_v6::ZxdgShellV6) -> Sh
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_implementation<U, R, CID, SID, SD>(
|
fn shell_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> zxdg_shell_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> zxdg_shell_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
@ -104,11 +103,10 @@ where
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
xdg_surface.set_user_data(
|
xdg_surface.set_user_data(Box::into_raw(Box::new((
|
||||||
Box::into_raw(Box::new((unsafe { wl_surface.clone_unchecked() }, unsafe {
|
unsafe { wl_surface.clone_unchecked() },
|
||||||
shell.clone_unchecked()
|
unsafe { shell.clone_unchecked() },
|
||||||
}))) as *mut _,
|
))) as *mut _);
|
||||||
);
|
|
||||||
evlh.register(
|
evlh.register(
|
||||||
&xdg_surface,
|
&xdg_surface,
|
||||||
surface_implementation(),
|
surface_implementation(),
|
||||||
|
@ -151,7 +149,8 @@ fn destroy_positioner(positioner: &zxdg_positioner_v6::ZxdgPositionerV6) {
|
||||||
fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
|
fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
|
||||||
zxdg_positioner_v6::Implementation {
|
zxdg_positioner_v6::Implementation {
|
||||||
destroy: |_, _, _, _| {},
|
destroy: |_, _, _, _| {},
|
||||||
set_size: |_, _, _, positioner, width, height| if width < 1 || height < 1 {
|
set_size: |_, _, _, positioner, width, height| {
|
||||||
|
if width < 1 || height < 1 {
|
||||||
positioner.post_error(
|
positioner.post_error(
|
||||||
zxdg_positioner_v6::Error::InvalidInput as u32,
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
"Invalid size for positioner.".into(),
|
"Invalid size for positioner.".into(),
|
||||||
|
@ -160,8 +159,10 @@ fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
|
||||||
let ptr = positioner.get_user_data();
|
let ptr = positioner.get_user_data();
|
||||||
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
let state = unsafe { &mut *(ptr as *mut PositionerState) };
|
||||||
state.rect_size = (width, height);
|
state.rect_size = (width, height);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
set_anchor_rect: |_, _, _, positioner, x, y, width, height| if width < 1 || height < 1 {
|
set_anchor_rect: |_, _, _, positioner, x, y, width, height| {
|
||||||
|
if width < 1 || height < 1 {
|
||||||
positioner.post_error(
|
positioner.post_error(
|
||||||
zxdg_positioner_v6::Error::InvalidInput as u32,
|
zxdg_positioner_v6::Error::InvalidInput as u32,
|
||||||
"Invalid size for positioner's anchor rectangle.".into(),
|
"Invalid size for positioner's anchor rectangle.".into(),
|
||||||
|
@ -175,6 +176,7 @@ fn positioner_implementation() -> zxdg_positioner_v6::Implementation<()> {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
set_anchor: |_, _, _, positioner, anchor| {
|
set_anchor: |_, _, _, positioner, anchor| {
|
||||||
use self::zxdg_positioner_v6::Anchor;
|
use self::zxdg_positioner_v6::Anchor;
|
||||||
|
@ -228,15 +230,15 @@ fn destroy_surface(surface: &zxdg_surface_v6::ZxdgSurfaceV6) {
|
||||||
let ptr = surface.get_user_data();
|
let ptr = surface.get_user_data();
|
||||||
surface.set_user_data(::std::ptr::null_mut());
|
surface.set_user_data(::std::ptr::null_mut());
|
||||||
// drop the state
|
// drop the state
|
||||||
let data =
|
let data = unsafe {
|
||||||
unsafe { Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6)) };
|
Box::from_raw(ptr as *mut (zxdg_surface_v6::ZxdgSurfaceV6, zxdg_shell_v6::ZxdgShellV6))
|
||||||
|
};
|
||||||
// explicit call to drop to not forget what we're doing here
|
// explicit call to drop to not forget what we're doing here
|
||||||
::std::mem::drop(data);
|
::std::mem::drop(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surface_implementation<U, R, CID, SID, SD>(
|
fn surface_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> zxdg_surface_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> zxdg_surface_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
@ -417,9 +419,9 @@ fn destroy_toplevel(surface: &zxdg_toplevel_v6::ZxdgToplevelV6) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions allowing to factor out a lot of the upcoming logic
|
// Utility functions allowing to factor out a lot of the upcoming logic
|
||||||
fn with_surface_toplevel_data<U, R, CID, SID, SD, F>(idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
fn with_surface_toplevel_data<U, R, CID, SID, SD, F>(
|
||||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F)
|
idata: &ShellSurfaceIData<U, R, CID, SID, SD>, toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, f: F
|
||||||
where
|
) where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -438,13 +440,11 @@ where
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xdg_handle_display_state_change<U, R, CID, SID, SD>(evlh: &mut EventLoopHandle,
|
fn xdg_handle_display_state_change<U, R, CID, SID, SD>(
|
||||||
idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
evlh: &mut EventLoopHandle, idata: &ShellSurfaceIData<U, R, CID, SID, SD>,
|
||||||
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
toplevel: &zxdg_toplevel_v6::ZxdgToplevelV6, maximized: Option<bool>, minimized: Option<bool>,
|
||||||
maximized: Option<bool>, minimized: Option<bool>,
|
fullscreen: Option<bool>, output: Option<&wl_output::WlOutput>,
|
||||||
fullscreen: Option<bool>,
|
) where
|
||||||
output: Option<&wl_output::WlOutput>)
|
|
||||||
where
|
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
CID: 'static,
|
CID: 'static,
|
||||||
|
@ -467,11 +467,10 @@ where
|
||||||
send_toplevel_configure(idata.compositor_token, toplevel, configure);
|
send_toplevel_configure(idata.compositor_token, toplevel, configure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_toplevel_configure<U, R, ID>(
|
||||||
pub fn send_toplevel_configure<U, R, ID>(token: CompositorToken<U, R, ID>,
|
token: CompositorToken<U, R, ID>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||||
resource: &zxdg_toplevel_v6::ZxdgToplevelV6,
|
configure: ToplevelConfigure,
|
||||||
configure: ToplevelConfigure)
|
) where
|
||||||
where
|
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
ID: 'static,
|
ID: 'static,
|
||||||
|
@ -497,9 +496,9 @@ where
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
|
fn make_toplevel_handle<U, R, H, SD>(
|
||||||
resource: &zxdg_toplevel_v6::ZxdgToplevelV6)
|
token: CompositorToken<U, R, H>, resource: &zxdg_toplevel_v6::ZxdgToplevelV6
|
||||||
-> super::ToplevelSurface<U, R, H, SD> {
|
) -> super::ToplevelSurface<U, R, H, SD> {
|
||||||
let ptr = resource.get_user_data();
|
let ptr = resource.get_user_data();
|
||||||
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
super::ToplevelSurface {
|
super::ToplevelSurface {
|
||||||
|
@ -511,8 +510,7 @@ fn make_toplevel_handle<U, R, H, SD>(token: CompositorToken<U, R, H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_implementation<U, R, CID, SID, SD>(
|
fn toplevel_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> zxdg_toplevel_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> zxdg_toplevel_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
@ -546,8 +544,9 @@ where
|
||||||
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
|
with_surface_toplevel_data(idata, toplevel, |toplevel_data| {
|
||||||
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
toplevel_data.parent = parent.map(|toplevel_surface_parent| {
|
||||||
let parent_ptr = toplevel_surface_parent.get_user_data();
|
let parent_ptr = toplevel_surface_parent.get_user_data();
|
||||||
let &(ref parent_surface, _) =
|
let &(ref parent_surface, _) = unsafe {
|
||||||
unsafe { &*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6)) };
|
&*(parent_ptr as *mut (wl_surface::WlSurface, zxdg_shell_v6::ZxdgShellV6))
|
||||||
|
};
|
||||||
unsafe { parent_surface.clone_unchecked() }
|
unsafe { parent_surface.clone_unchecked() }
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -620,10 +619,9 @@ fn destroy_popup(surface: &zxdg_popup_v6::ZxdgPopupV6) {
|
||||||
::std::mem::drop(data);
|
::std::mem::drop(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_popup_configure<U, R, ID>(token: CompositorToken<U, R, ID>,
|
pub(crate) fn send_popup_configure<U, R, ID>(
|
||||||
resource: &zxdg_popup_v6::ZxdgPopupV6,
|
token: CompositorToken<U, R, ID>, resource: &zxdg_popup_v6::ZxdgPopupV6, configure: PopupConfigure
|
||||||
configure: PopupConfigure)
|
) where
|
||||||
where
|
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
ID: 'static,
|
ID: 'static,
|
||||||
|
@ -641,8 +639,9 @@ where
|
||||||
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
.expect("xdg_toplevel exists but surface has not shell_surface role?!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_popup_handle<U, R, H, SD>(token: CompositorToken<U, R, H>, resource: &zxdg_popup_v6::ZxdgPopupV6)
|
fn make_popup_handle<U, R, H, SD>(
|
||||||
-> super::PopupSurface<U, R, H, SD> {
|
token: CompositorToken<U, R, H>, resource: &zxdg_popup_v6::ZxdgPopupV6
|
||||||
|
) -> super::PopupSurface<U, R, H, SD> {
|
||||||
let ptr = resource.get_user_data();
|
let ptr = resource.get_user_data();
|
||||||
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
let &(ref wl_surface, _, _) = unsafe { &*(ptr as *mut ShellSurfaceUserData) };
|
||||||
super::PopupSurface {
|
super::PopupSurface {
|
||||||
|
@ -654,8 +653,7 @@ fn make_popup_handle<U, R, H, SD>(token: CompositorToken<U, R, H>, resource: &zx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popup_implementation<U, R, CID, SID, SD>(
|
fn popup_implementation<U, R, CID, SID, SD>(
|
||||||
)
|
) -> zxdg_popup_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
||||||
-> zxdg_popup_v6::Implementation<ShellSurfaceIData<U, R, CID, SID, SD>>
|
|
||||||
where
|
where
|
||||||
U: 'static,
|
U: 'static,
|
||||||
R: Role<ShellSurfaceRole> + 'static,
|
R: Role<ShellSurfaceRole> + 'static,
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
//!
|
//!
|
||||||
//! If you are already using an handler for this signal, you probably don't want to use this handler.
|
//! If you are already using an handler for this signal, you probably don't want to use this handler.
|
||||||
|
|
||||||
|
|
||||||
use self::pool::{Pool, ResizeError};
|
use self::pool::{Pool, ResizeError};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -89,8 +88,9 @@ pub struct ShmGlobalData {
|
||||||
/// The global is directly registered into the eventloop, and this function
|
/// The global is directly registered into the eventloop, and this function
|
||||||
/// returns the global handle, in case you whish to remove this global in
|
/// returns the global handle, in case you whish to remove this global in
|
||||||
/// the future.
|
/// the future.
|
||||||
pub fn init_shm_global<L>(evl: &mut EventLoop, mut formats: Vec<wl_shm::Format>, logger: L)
|
pub fn init_shm_global<L>(
|
||||||
-> Global<wl_shm::WlShm, ShmGlobalData>
|
evl: &mut EventLoop, mut formats: Vec<wl_shm::Format>, logger: L
|
||||||
|
) -> Global<wl_shm::WlShm, ShmGlobalData>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>,
|
L: Into<Option<::slog::Logger>>,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
use nix::{libc, unistd};
|
use nix::{libc, unistd};
|
||||||
use nix::sys::mman;
|
use nix::sys::mman;
|
||||||
use nix::sys::signal::{self, SigAction, SigHandler, Signal};
|
use nix::sys::signal::{self, SigAction, SigHandler, Signal};
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
minimum-vagga: v0.5.0
|
||||||
|
|
||||||
|
containers:
|
||||||
|
base:
|
||||||
|
auto-clean: true
|
||||||
|
setup:
|
||||||
|
- !UbuntuRelease { codename: artful }
|
||||||
|
- !UbuntuUniverse
|
||||||
|
- !Install [build-essential, wget, curl, pkg-config, file, openssl, sudo, ca-certificates, libssl-dev, cmake, libudev-dev, libgbm-dev, libxkbcommon-dev, libegl1-mesa-dev, libwayland-dev, libinput-dev]
|
||||||
|
|
||||||
|
stable:
|
||||||
|
auto-clean: true
|
||||||
|
environ:
|
||||||
|
HOME: /work/.vagga/stable-home
|
||||||
|
PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/stable-home/.cargo/bin:/work/.vagga/stable-home/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
XDG_RUNTIME_DIR: /work/.vagga/stable-home
|
||||||
|
setup:
|
||||||
|
- !Container base
|
||||||
|
- !Env HOME: /work/.vagga/stable-home
|
||||||
|
- !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain stable --no-modify-path
|
||||||
|
|
||||||
|
beta:
|
||||||
|
auto-clean: true
|
||||||
|
environ:
|
||||||
|
HOME: /work/.vagga/beta-home
|
||||||
|
PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/beta-home/.cargo/bin:/work/.vagga/beta-home/.rustup/toolchains/beta-x86_64-unknown-linux-gnu/bin/
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
XDG_RUNTIME_DIR: /work/.vagga/beta-home
|
||||||
|
setup:
|
||||||
|
- !Container base
|
||||||
|
- !Env HOME: /work/.vagga/beta-home
|
||||||
|
- !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain beta --no-modify-path
|
||||||
|
|
||||||
|
nightly:
|
||||||
|
auto-clean: true
|
||||||
|
environ:
|
||||||
|
HOME: /work/.vagga/nightly-home
|
||||||
|
PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/work/.vagga/nightly-home/.cargo/bin:/work/.vagga/nightly-home/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
XDG_RUNTIME_DIR: /work/.vagga/nightly-home
|
||||||
|
setup:
|
||||||
|
- !Container base
|
||||||
|
- !Env HOME: /work/.vagga/nightly-home
|
||||||
|
- !Sh curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host x86_64-unknown-linux-gnu --default-toolchain nightly --no-modify-path
|
||||||
|
|
||||||
|
commands:
|
||||||
|
update-stable: !Command
|
||||||
|
description: Update container
|
||||||
|
container: stable
|
||||||
|
run: rustup update
|
||||||
|
|
||||||
|
update-beta: !Command
|
||||||
|
description: Update container
|
||||||
|
container: beta
|
||||||
|
run: rustup update
|
||||||
|
|
||||||
|
update-nightly: !Command
|
||||||
|
description: Update container
|
||||||
|
container: nightly
|
||||||
|
run: |
|
||||||
|
rustup update
|
||||||
|
|
||||||
|
cargo-stable: !Command
|
||||||
|
description: Run cargo
|
||||||
|
container: stable
|
||||||
|
run: ["/work/.vagga/stable-home/.cargo/bin/cargo"]
|
||||||
|
|
||||||
|
cargo-beta: !Command
|
||||||
|
description: Run cargo
|
||||||
|
container: beta
|
||||||
|
run: ["/work/.vagga/beta-home/.cargo/bin/cargo"]
|
||||||
|
|
||||||
|
cargo-nightly: !Command
|
||||||
|
description: Run cargo
|
||||||
|
container: nightly
|
||||||
|
run: ["/work/.vagga/nightly-home/.cargo/bin/cargo"]
|
Loading…
Reference in New Issue