Merge pull request #90 from Smithay/anvil
move examples into our own compositor
This commit is contained in:
commit
2b8d1aa663
|
@ -47,6 +47,8 @@ env:
|
|||
- FEATURES="default"
|
||||
# test all features simultaneously
|
||||
- FEATURES="all"
|
||||
# test our house compositor
|
||||
- FEATURES="anvil"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
@ -85,6 +87,9 @@ script:
|
|||
vagga cargo-$TRAVIS_RUST_VERSION install -f clippy &&
|
||||
vagga cargo-$TRAVIS_RUST_VERSION clippy --all-features -- -D warnings
|
||||
;;
|
||||
"anvil")
|
||||
vagga cargo-$TRAVIS_RUST_VERSION test -p anvil --all-features
|
||||
;;
|
||||
*)
|
||||
vagga cargo-$TRAVIS_RUST_VERSION test --lib --doc --tests --no-default-features --features "$FEATURES" &&
|
||||
vagga cargo-$TRAVIS_RUST_VERSION doc --no-deps --no-default-features --features "$FEATURES"
|
||||
|
|
|
@ -6,6 +6,9 @@ license = "MIT"
|
|||
description = "Smithay is a library for writing wayland compositors."
|
||||
repository = "https://github.com/Smithay/smithay"
|
||||
|
||||
[workspace]
|
||||
members = [ "anvil" ]
|
||||
|
||||
[dependencies]
|
||||
wayland-server = "0.20.2"
|
||||
wayland-sys = "0.20.2"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "anvil"
|
||||
version = "0.0.1"
|
||||
authors = ["Victor Berger <victor.berger@m4x.org>", "Drakulix (Victor Brekenfeld)"]
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
slog = { version = "2.1.1" }
|
||||
slog-term = "2.3"
|
||||
slog-async = "2.2"
|
||||
rand = "0.3"
|
||||
glium = { version = "0.19.0", default-features = false }
|
||||
wayland-server = "0.20"
|
||||
xkbcommon = "0.2.1"
|
||||
|
||||
[dependencies.smithay]
|
||||
path = ".."
|
||||
default-features = false
|
||||
features = [ "renderer_glium" ]
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.9"
|
||||
|
||||
[features]
|
||||
default = [ "winit", "tty_launch", "udev" ]
|
||||
winit = [ "smithay/backend_winit" ]
|
||||
tty_launch = [ "smithay/backend_libinput", "smithay/backend_drm" ]
|
||||
udev = [ "tty_launch", "smithay/backend_udev" ]
|
||||
logind = [ "smithay/backend_session_logind" ]
|
||||
|
|
@ -6,9 +6,15 @@ use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
|||
use smithay::backend::graphics::egl::error::Result as EGLResult;
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages, EGLWaylandExtensions, Format};
|
||||
use smithay::backend::graphics::glium::GliumGraphicsBackend;
|
||||
use smithay::wayland::compositor::{SubsurfaceRole, TraversalAction};
|
||||
use smithay::wayland::compositor::roles::Role;
|
||||
use smithay::wayland_server::Display;
|
||||
|
||||
use std::cell::Ref;
|
||||
use std::ops::Deref;
|
||||
use wayland_server::Display;
|
||||
|
||||
use slog::Logger;
|
||||
|
||||
use shell::{Buffer, MyCompositorToken, MyWindowMap};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vertex {
|
||||
|
@ -190,3 +196,95 @@ impl<G: EGLWaylandExtensions + EGLGraphicsBackend + 'static> EGLWaylandExtension
|
|||
self.display.bind_wl_display(display)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||
pub fn draw_windows(&self, window_map: &MyWindowMap, compositor_token: MyCompositorToken, log: &Logger) {
|
||||
let mut frame = self.draw();
|
||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||
// redraw the frame, in a simple but inneficient way
|
||||
{
|
||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
||||
window_map.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
|
||||
compositor_token
|
||||
.with_surface_tree_upward(
|
||||
wl_surface,
|
||||
initial_place,
|
||||
|_surface, attributes, role, &(mut x, mut y)| {
|
||||
// there is actually something to draw !
|
||||
if attributes.user_data.texture.is_none() {
|
||||
let mut remove = false;
|
||||
match attributes.user_data.buffer {
|
||||
Some(Buffer::Egl { ref images }) => {
|
||||
match images.format {
|
||||
Format::RGB | Format::RGBA => {
|
||||
attributes.user_data.texture =
|
||||
self.texture_from_egl(&images);
|
||||
}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.texture = None;
|
||||
remove = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(Buffer::Shm { ref data, ref size }) => {
|
||||
attributes.user_data.texture =
|
||||
Some(self.texture_from_mem(data, *size));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if remove {
|
||||
attributes.user_data.buffer = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref texture) = attributes.user_data.texture {
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
}
|
||||
self.render_texture(
|
||||
&mut frame,
|
||||
texture,
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => images.y_inverted,
|
||||
Buffer::Shm { .. } => false,
|
||||
},
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => (images.width, images.height),
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
},
|
||||
(x, y),
|
||||
screen_dimensions,
|
||||
::glium::Blend {
|
||||
color: ::glium::BlendingFunction::Addition {
|
||||
source: ::glium::LinearBlendingFactor::One,
|
||||
destination:
|
||||
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
alpha: ::glium::BlendingFunction::Addition {
|
||||
source: ::glium::LinearBlendingFactor::One,
|
||||
destination:
|
||||
::glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
TraversalAction::DoChildren((x, y))
|
||||
} else {
|
||||
// we are not display, so our children are neither
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
if let Err(err) = frame.finish() {
|
||||
error!(log, "Error during rendering: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
use std::cell::RefCell;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use slog::Logger;
|
||||
|
||||
#[cfg(feature = "udev")]
|
||||
use smithay::backend::session::auto::AutoSession;
|
||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
|
||||
PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent,
|
||||
PointerMotionEvent};
|
||||
use smithay::wayland::seat::{keysyms as xkb, KeyboardHandle, Keysym, ModifiersState, PointerHandle};
|
||||
use smithay::wayland_server::protocol::wl_pointer;
|
||||
|
||||
use shell::MyWindowMap;
|
||||
|
||||
pub struct AnvilInputHandler {
|
||||
log: Logger,
|
||||
pointer: PointerHandle,
|
||||
keyboard: KeyboardHandle,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
screen_size: (u32, u32),
|
||||
serial: u32,
|
||||
#[cfg(feature = "udev")]
|
||||
session: Option<AutoSession>,
|
||||
running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AnvilInputHandler {
|
||||
pub fn new(
|
||||
log: Logger,
|
||||
pointer: PointerHandle,
|
||||
keyboard: KeyboardHandle,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
screen_size: (u32, u32),
|
||||
running: Arc<AtomicBool>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
) -> AnvilInputHandler {
|
||||
AnvilInputHandler {
|
||||
log,
|
||||
pointer,
|
||||
keyboard,
|
||||
window_map,
|
||||
screen_size,
|
||||
running,
|
||||
pointer_location,
|
||||
serial: 1,
|
||||
#[cfg(feature = "udev")]
|
||||
session: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "udev")]
|
||||
pub fn new_with_session(
|
||||
log: Logger,
|
||||
pointer: PointerHandle,
|
||||
keyboard: KeyboardHandle,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
screen_size: (u32, u32),
|
||||
running: Arc<AtomicBool>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
session: AutoSession,
|
||||
) -> AnvilInputHandler {
|
||||
AnvilInputHandler {
|
||||
log,
|
||||
pointer,
|
||||
keyboard,
|
||||
window_map,
|
||||
screen_size,
|
||||
running,
|
||||
pointer_location,
|
||||
serial: 1,
|
||||
session: Some(session),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_serial(&mut self) -> u32 {
|
||||
self.serial += 1;
|
||||
self.serial
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: InputBackend> InputHandler<B> for AnvilInputHandler {
|
||||
fn on_seat_created(&mut self, _: &input::Seat) {
|
||||
/* currently we just create a single static one */
|
||||
}
|
||||
|
||||
fn on_seat_destroyed(&mut self, _: &input::Seat) {
|
||||
/* currently we just create a single static one */
|
||||
}
|
||||
|
||||
fn on_seat_changed(&mut self, _: &input::Seat) {
|
||||
/* currently we just create a single static one */
|
||||
}
|
||||
|
||||
fn on_keyboard_key(&mut self, _: &input::Seat, evt: B::KeyboardKeyEvent) {
|
||||
let keycode = evt.key_code();
|
||||
let state = evt.state();
|
||||
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||
let serial = self.next_serial();
|
||||
let log = &self.log;
|
||||
let time = Event::time(&evt);
|
||||
let mut action = KeyAction::None;
|
||||
self.keyboard
|
||||
.input(keycode, state, serial, time, |modifiers, keysym| {
|
||||
debug!(log, "keysym";
|
||||
"state" => format!("{:?}", state),
|
||||
"mods" => format!("{:?}", modifiers),
|
||||
"keysym" => ::xkbcommon::xkb::keysym_get_name(keysym)
|
||||
);
|
||||
action = process_keyboard_shortcut(modifiers, keysym);
|
||||
// forward to client only if action == KeyAction::Forward
|
||||
// both for pressed and released, to avoid inconsistencies
|
||||
if let KeyAction::Forward = action {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
if let KeyState::Released = state {
|
||||
// only process special actions on key press, not release
|
||||
return;
|
||||
}
|
||||
match action {
|
||||
KeyAction::Quit => {
|
||||
info!(self.log, "Quitting.");
|
||||
self.running.store(false, Ordering::SeqCst);
|
||||
}
|
||||
#[cfg(feature = "tty_lauch")]
|
||||
KeyAction::VtSwitch(vt) => if let Some(ref mut session) = self.session {
|
||||
info!(log, "Trying to switch to vt {}", vt);
|
||||
if let Err(err) = session.change_vt(vt) {
|
||||
error!(log, "Error switching to vt {}: {}", vt, err);
|
||||
}
|
||||
},
|
||||
KeyAction::Run(cmd) => {
|
||||
info!(self.log, "Starting program"; "cmd" => cmd.clone());
|
||||
if let Err(e) = Command::new(&cmd).spawn() {
|
||||
error!(log,
|
||||
"Failed to start program";
|
||||
"cmd" => cmd,
|
||||
"err" => format!("{:?}", e)
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_pointer_move(&mut self, _: &input::Seat, evt: B::PointerMotionEvent) {
|
||||
let (x, y) = (evt.delta_x(), evt.delta_y());
|
||||
let serial = self.next_serial();
|
||||
let mut location = self.pointer_location.borrow_mut();
|
||||
location.0 += x as f64;
|
||||
location.1 += y as f64;
|
||||
// clamp to screen limits
|
||||
// this event is never generated by winit so self.screen_size is relevant
|
||||
location.0 = (location.0).max(0.0).min(self.screen_size.0 as f64);
|
||||
location.1 = (location.1).max(0.0).min(self.screen_size.1 as f64);
|
||||
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: B::PointerMotionAbsoluteEvent) {
|
||||
// different cases depending on the context:
|
||||
let (x, y) = {
|
||||
#[cfg(feature = "udev")]
|
||||
{
|
||||
if self.session.is_some() {
|
||||
// we are started on a tty
|
||||
let (ux, uy) = evt.position_transformed(self.screen_size);
|
||||
(ux as f64, uy as f64)
|
||||
} else {
|
||||
// we are started in winit
|
||||
evt.position()
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "udev"))]
|
||||
{
|
||||
evt.position()
|
||||
}
|
||||
};
|
||||
*self.pointer_location.borrow_mut() = (x, y);
|
||||
let serial = self.next_serial();
|
||||
let under = self.window_map
|
||||
.borrow()
|
||||
.get_surface_under((x as f64, y as f64));
|
||||
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: B::PointerButtonEvent) {
|
||||
let serial = self.next_serial();
|
||||
let button = match evt.button() {
|
||||
input::MouseButton::Left => 0x110,
|
||||
input::MouseButton::Right => 0x111,
|
||||
input::MouseButton::Middle => 0x112,
|
||||
input::MouseButton::Other(b) => b as u32,
|
||||
};
|
||||
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: B::PointerAxisEvent) {
|
||||
let source = match evt.source() {
|
||||
input::AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
|
||||
input::AxisSource::Finger => wl_pointer::AxisSource::Finger,
|
||||
input::AxisSource::Wheel | input::AxisSource::WheelTilt => wl_pointer::AxisSource::Wheel,
|
||||
};
|
||||
let horizontal_amount = evt.amount(&input::Axis::Horizontal)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0);
|
||||
let vertical_amount = evt.amount(&input::Axis::Vertical)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0);
|
||||
let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal);
|
||||
let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);
|
||||
|
||||
{
|
||||
let mut event = self.pointer.axis();
|
||||
event.source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
event.value(
|
||||
wl_pointer::Axis::HorizontalScroll,
|
||||
horizontal_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
event.discrete(wl_pointer::Axis::HorizontalScroll, discrete as i32);
|
||||
}
|
||||
} else if source == wl_pointer::AxisSource::Finger {
|
||||
event.stop(wl_pointer::Axis::HorizontalScroll, evt.time());
|
||||
}
|
||||
if vertical_amount != 0.0 {
|
||||
event.value(
|
||||
wl_pointer::Axis::VerticalScroll,
|
||||
vertical_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
event.discrete(wl_pointer::Axis::VerticalScroll, discrete as i32);
|
||||
}
|
||||
} else if source == wl_pointer::AxisSource::Finger {
|
||||
event.stop(wl_pointer::Axis::VerticalScroll, evt.time());
|
||||
}
|
||||
event.done();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_touch_down(&mut self, _: &input::Seat, _: B::TouchDownEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_motion(&mut self, _: &input::Seat, _: B::TouchMotionEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_up(&mut self, _: &input::Seat, _: B::TouchUpEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_cancel(&mut self, _: &input::Seat, _: B::TouchCancelEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_frame(&mut self, _: &input::Seat, _: B::TouchFrameEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_input_config_changed(&mut self, _: &mut B::InputConfig) {
|
||||
/* not done in this example */
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible results of a keyboard action
|
||||
enum KeyAction {
|
||||
/// Quit the compositor
|
||||
Quit,
|
||||
/// Trigger a vt-switch
|
||||
VtSwitch(i32),
|
||||
/// run a command
|
||||
Run(String),
|
||||
/// Forward the key to the client
|
||||
Forward,
|
||||
/// Do nothing more
|
||||
None,
|
||||
}
|
||||
|
||||
fn process_keyboard_shortcut(modifiers: &ModifiersState, keysym: Keysym) -> KeyAction {
|
||||
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace {
|
||||
// ctrl+alt+backspace = quit
|
||||
KeyAction::Quit
|
||||
} else if modifiers.logo && keysym == xkb::KEY_q {
|
||||
// logo + q = quit
|
||||
KeyAction::Quit
|
||||
} else if keysym >= xkb::KEY_XF86Switch_VT_1 && keysym <= xkb::KEY_XF86Switch_VT_12 {
|
||||
// VTSwicth
|
||||
KeyAction::VtSwitch((keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32)
|
||||
} else if modifiers.logo && keysym == xkb::KEY_Return {
|
||||
// run terminal
|
||||
KeyAction::Run("weston-terminal".into())
|
||||
} else {
|
||||
KeyAction::Forward
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#[macro_use]
|
||||
extern crate glium;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_async;
|
||||
extern crate slog_term;
|
||||
#[macro_use(define_roles)]
|
||||
extern crate smithay;
|
||||
extern crate xkbcommon;
|
||||
|
||||
use slog::Drain;
|
||||
use smithay::wayland_server::Display;
|
||||
|
||||
mod glium_drawer;
|
||||
mod shell;
|
||||
#[cfg(feature = "udev")]
|
||||
mod udev;
|
||||
mod window_map;
|
||||
#[cfg(feature = "winit")]
|
||||
mod winit;
|
||||
mod input_handler;
|
||||
#[cfg(feature = "tty_launch")]
|
||||
mod raw_drm;
|
||||
|
||||
static POSSIBLE_BACKENDS: &'static [&'static str] = &[
|
||||
#[cfg(feature = "winit")]
|
||||
"--winit : Run anvil as a X11 or Wayland client using winit.",
|
||||
#[cfg(feature = "tty_launch")]
|
||||
"--tty-raw : Run anvil as a raw DRM client (requires root).",
|
||||
#[cfg(feature = "udev")]
|
||||
"--tty-udev : Run anvil as a tty udev client (requires root if without logind).",
|
||||
];
|
||||
|
||||
fn main() {
|
||||
// A logger facility, here we use the terminal here
|
||||
let log = slog::Logger::root(
|
||||
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
||||
o!(),
|
||||
);
|
||||
|
||||
let (mut display, mut event_loop) = Display::new();
|
||||
|
||||
let arg = ::std::env::args().skip(1).next();
|
||||
match arg.as_ref().map(|s| &s[..]) {
|
||||
#[cfg(feature = "winit")]
|
||||
Some("--winit") => {
|
||||
info!(log, "Starting anvil with winit backend");
|
||||
if let Err(()) = winit::run_winit(&mut display, &mut event_loop, log.clone()) {
|
||||
crit!(log, "Failed to initialize winit backend.");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "tty_launch")]
|
||||
Some("--tty-raw") => {
|
||||
info!(log, "Starting anvil on a tty using raw DRM");
|
||||
if let Err(()) = raw_drm::run_raw_drm(display, event_loop, log.clone()) {
|
||||
crit!(log, "Failed to initialize tty backend.");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "udev")]
|
||||
Some("--tty-udev") => {
|
||||
info!(log, "Starting anvil on a tty using udev");
|
||||
if let Err(()) = udev::run_udev(display, event_loop, log.clone()) {
|
||||
crit!(log, "Failed to initialize tty backend.");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("USAGE: anvil --backend");
|
||||
println!("");
|
||||
println!("Possible backends are:");
|
||||
for b in POSSIBLE_BACKENDS {
|
||||
println!("\t{}", b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay::drm::Device as BasicDevice;
|
||||
use smithay::drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
use smithay::drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
use smithay::drm::control::crtc;
|
||||
use smithay::drm::control::encoder::Info as EncoderInfo;
|
||||
use smithay::drm::result::Error as DrmError;
|
||||
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||
use smithay::backend::graphics::egl::wayland::EGLWaylandExtensions;
|
||||
use smithay::wayland::compositor::CompositorToken;
|
||||
use smithay::wayland::shm::init_shm_global;
|
||||
use smithay::wayland_server::{Display, EventLoop};
|
||||
|
||||
use glium::Surface;
|
||||
use slog::Logger;
|
||||
|
||||
use glium_drawer::GliumDrawer;
|
||||
use shell::{init_shell, MyWindowMap, Roles, SurfaceData};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Card(File);
|
||||
|
||||
impl AsRawFd for Card {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicDevice for Card {}
|
||||
impl ControlDevice for Card {}
|
||||
|
||||
pub fn run_raw_drm(mut display: Display, mut event_loop: EventLoop, log: Logger) -> Result<(), ()> {
|
||||
/*
|
||||
* Initialize the drm backend
|
||||
*/
|
||||
// "Find" a suitable drm device
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.write(true);
|
||||
let mut device = DrmDevice::new(
|
||||
Card(options.clone().open("/dev/dri/card0").unwrap()),
|
||||
log.clone(),
|
||||
).unwrap();
|
||||
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = device.resource_handles().unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_info = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.unwrap();
|
||||
|
||||
// Use the first encoder
|
||||
let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||
|
||||
// use the connected crtc if any
|
||||
let crtc = encoder_info.current_crtc()
|
||||
// or use the first one that is compatible with the encoder
|
||||
.unwrap_or_else(||
|
||||
*res_handles.filter_crtcs(encoder_info.possible_crtcs())
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap());
|
||||
|
||||
// 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.)
|
||||
|
||||
// Initialize the hardware backend
|
||||
let renderer = GliumDrawer::from(
|
||||
device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap(),
|
||||
);
|
||||
{
|
||||
/*
|
||||
* Initialize glium
|
||||
*/
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
let egl_display = Rc::new(RefCell::new(
|
||||
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||
info!(log, "EGL hardware-acceleration enabled");
|
||||
Some(egl_display)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
));
|
||||
|
||||
/*
|
||||
* Initialize the globals
|
||||
*/
|
||||
|
||||
init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
|
||||
|
||||
let (compositor_token, _, _, window_map) =
|
||||
init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
|
||||
|
||||
/*
|
||||
* Add a listening socket:
|
||||
*/
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
println!("Listening on socket: {}", name);
|
||||
|
||||
/*
|
||||
* Register the DrmDevice on the EventLoop
|
||||
*/
|
||||
let _source = drm_device_bind(
|
||||
&event_loop.token(),
|
||||
device,
|
||||
DrmHandlerImpl {
|
||||
compositor_token,
|
||||
window_map: window_map.clone(),
|
||||
drawer: renderer,
|
||||
logger: log,
|
||||
},
|
||||
).map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
event_loop.dispatch(Some(16)).unwrap();
|
||||
display.flush_clients();
|
||||
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
drawer: GliumDrawer<DrmBackend<Card>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<Card> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<Card>,
|
||||
_crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
self.drawer.draw_windows(
|
||||
&*self.window_map.borrow(),
|
||||
self.compositor_token,
|
||||
&self.logger,
|
||||
);
|
||||
}
|
||||
|
||||
fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
use super::{SurfaceKind, WindowMap};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use glium::texture::Texture2d;
|
||||
|
||||
use rand;
|
||||
|
||||
use smithay::backend::graphics::egl::wayland::{BufferAccessError, Format};
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages};
|
||||
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceEvent};
|
||||
|
@ -9,98 +14,18 @@ use smithay::wayland::shell::xdg::{xdg_shell_init, PopupConfigure, ShellState as
|
|||
use smithay::wayland::shell::legacy::{wl_shell_init, ShellRequest, ShellState as WlShellState,
|
||||
ShellSurfaceKind, ShellSurfaceRole};
|
||||
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use wayland_server::{Display, LoopToken, Resource};
|
||||
use wayland_server::protocol::{wl_buffer, wl_callback, wl_shell_surface, wl_surface};
|
||||
use smithay::wayland_server::{Display, LoopToken, Resource};
|
||||
use smithay::wayland_server::protocol::{wl_buffer, wl_callback, wl_shell_surface, wl_surface};
|
||||
|
||||
use window_map::{Kind as SurfaceKind, WindowMap};
|
||||
|
||||
define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole<()>] );
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SurfaceData {
|
||||
pub buffer: Option<Buffer>,
|
||||
pub texture: Option<Texture2d>,
|
||||
}
|
||||
|
||||
pub enum Buffer {
|
||||
Egl { images: EGLImages },
|
||||
Shm { data: Vec<u8>, size: (u32, u32) },
|
||||
}
|
||||
|
||||
fn surface_commit(
|
||||
surface: &Resource<wl_surface::WlSurface>,
|
||||
token: CompositorToken<SurfaceData, Roles>,
|
||||
display: &RefCell<Option<EGLDisplay>>,
|
||||
) {
|
||||
// we retrieve the contents of the associated buffer and copy it
|
||||
token.with_surface_data(surface, |attributes| {
|
||||
match attributes.buffer.take() {
|
||||
Some(Some((buffer, (_x, _y)))) => {
|
||||
// we ignore hotspot coordinates in this simple example
|
||||
match if let Some(display) = display.borrow().as_ref() {
|
||||
display.egl_buffer_contents(buffer)
|
||||
} else {
|
||||
Err(BufferAccessError::NotManaged(buffer))
|
||||
} {
|
||||
Ok(images) => {
|
||||
match images.format {
|
||||
Format::RGB => {}
|
||||
Format::RGBA => {}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.buffer = None;
|
||||
attributes.user_data.texture = None;
|
||||
return;
|
||||
}
|
||||
};
|
||||
attributes.user_data.texture = None;
|
||||
attributes.user_data.buffer = Some(Buffer::Egl { images });
|
||||
}
|
||||
Err(BufferAccessError::NotManaged(buffer)) => {
|
||||
shm_buffer_contents(&buffer, |slice, data| {
|
||||
let offset = data.offset as usize;
|
||||
let stride = data.stride as usize;
|
||||
let width = data.width as usize;
|
||||
let height = data.height as usize;
|
||||
let mut new_vec = Vec::with_capacity(width * height * 4);
|
||||
for i in 0..height {
|
||||
new_vec
|
||||
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
||||
}
|
||||
attributes.user_data.texture = None;
|
||||
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
|
||||
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
|
||||
buffer.send(wl_buffer::Event::Release);
|
||||
}
|
||||
Err(err) => panic!("EGL error: {}", err),
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
// erase the contents
|
||||
attributes.user_data.buffer = None;
|
||||
attributes.user_data.texture = None;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
|
||||
attrs
|
||||
.user_data
|
||||
.buffer
|
||||
.as_ref()
|
||||
.map(|ref buffer| match **buffer {
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
Buffer::Egl { ref images } => (images.width, images.height),
|
||||
})
|
||||
.map(|(x, y)| (x as i32, y as i32))
|
||||
}
|
||||
|
||||
pub type MyWindowMap =
|
||||
WindowMap<SurfaceData, Roles, (), (), fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>>;
|
||||
|
||||
pub type MyCompositorToken = CompositorToken<SurfaceData, Roles>;
|
||||
|
||||
pub fn init_shell(
|
||||
display: &mut Display,
|
||||
looptoken: LoopToken,
|
||||
|
@ -199,3 +124,84 @@ pub fn init_shell(
|
|||
window_map,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SurfaceData {
|
||||
pub buffer: Option<Buffer>,
|
||||
pub texture: Option<Texture2d>,
|
||||
}
|
||||
|
||||
pub enum Buffer {
|
||||
Egl { images: EGLImages },
|
||||
Shm { data: Vec<u8>, size: (u32, u32) },
|
||||
}
|
||||
|
||||
fn surface_commit(
|
||||
surface: &Resource<wl_surface::WlSurface>,
|
||||
token: CompositorToken<SurfaceData, Roles>,
|
||||
display: &RefCell<Option<EGLDisplay>>,
|
||||
) {
|
||||
// we retrieve the contents of the associated buffer and copy it
|
||||
token.with_surface_data(surface, |attributes| {
|
||||
match attributes.buffer.take() {
|
||||
Some(Some((buffer, (_x, _y)))) => {
|
||||
// we ignore hotspot coordinates in this simple example
|
||||
match if let Some(display) = display.borrow().as_ref() {
|
||||
display.egl_buffer_contents(buffer)
|
||||
} else {
|
||||
Err(BufferAccessError::NotManaged(buffer))
|
||||
} {
|
||||
Ok(images) => {
|
||||
match images.format {
|
||||
Format::RGB => {}
|
||||
Format::RGBA => {}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.buffer = None;
|
||||
attributes.user_data.texture = None;
|
||||
return;
|
||||
}
|
||||
};
|
||||
attributes.user_data.texture = None;
|
||||
attributes.user_data.buffer = Some(Buffer::Egl { images });
|
||||
}
|
||||
Err(BufferAccessError::NotManaged(buffer)) => {
|
||||
shm_buffer_contents(&buffer, |slice, data| {
|
||||
let offset = data.offset as usize;
|
||||
let stride = data.stride as usize;
|
||||
let width = data.width as usize;
|
||||
let height = data.height as usize;
|
||||
let mut new_vec = Vec::with_capacity(width * height * 4);
|
||||
for i in 0..height {
|
||||
new_vec
|
||||
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
||||
}
|
||||
attributes.user_data.texture = None;
|
||||
attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) });
|
||||
}).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!");
|
||||
buffer.send(wl_buffer::Event::Release);
|
||||
}
|
||||
Err(err) => panic!("EGL error: {}", err),
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
// erase the contents
|
||||
attributes.user_data.buffer = None;
|
||||
attributes.user_data.texture = None;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_size(attrs: &SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)> {
|
||||
attrs
|
||||
.user_data
|
||||
.buffer
|
||||
.as_ref()
|
||||
.map(|ref buffer| match **buffer {
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
Buffer::Egl { ref images } => (images.width, images.height),
|
||||
})
|
||||
.map(|(x, y)| (x as i32, y as i32))
|
||||
}
|
|
@ -0,0 +1,350 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error as IoError;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use glium::Surface;
|
||||
|
||||
use smithay::image::{ImageBuffer, Rgba};
|
||||
|
||||
use slog::Logger;
|
||||
|
||||
use smithay::drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
use smithay::drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
use smithay::drm::control::crtc;
|
||||
use smithay::drm::control::encoder::Info as EncoderInfo;
|
||||
use smithay::drm::result::Error as DrmError;
|
||||
use smithay::backend::drm::{DevPath, DrmBackend, DrmDevice, DrmHandler};
|
||||
use smithay::backend::graphics::GraphicsBackend;
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions};
|
||||
use smithay::backend::input::InputBackend;
|
||||
use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface};
|
||||
use smithay::backend::session::{Session, SessionNotifier};
|
||||
use smithay::backend::session::auto::{auto_session_bind, AutoSession};
|
||||
use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler};
|
||||
use smithay::wayland::compositor::CompositorToken;
|
||||
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
|
||||
use smithay::wayland::seat::Seat;
|
||||
use smithay::wayland::shm::init_shm_global;
|
||||
use smithay::wayland_server::{Display, EventLoop};
|
||||
use smithay::wayland_server::commons::downcast_impl;
|
||||
use smithay::wayland_server::protocol::wl_output;
|
||||
use smithay::input::Libinput;
|
||||
|
||||
use glium_drawer::GliumDrawer;
|
||||
use shell::{init_shell, MyWindowMap, Roles, SurfaceData};
|
||||
use input_handler::AnvilInputHandler;
|
||||
|
||||
pub fn run_udev(mut display: Display, mut event_loop: EventLoop, log: Logger) -> Result<(), ()> {
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
info!(log, "Listening on wayland socket"; "name" => name.clone());
|
||||
::std::env::set_var("WAYLAND_DISPLAY", name);
|
||||
|
||||
let active_egl_context = Rc::new(RefCell::new(None));
|
||||
|
||||
let display = Rc::new(RefCell::new(display));
|
||||
|
||||
/*
|
||||
* Initialize the compositor
|
||||
*/
|
||||
init_shm_global(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
vec![],
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let (compositor_token, _, _, window_map) = init_shell(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
log.clone(),
|
||||
active_egl_context.clone(),
|
||||
);
|
||||
|
||||
/*
|
||||
* Initialize session
|
||||
*/
|
||||
let (session, mut notifier) = AutoSession::new(log.clone()).ok_or(())?;
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
|
||||
|
||||
/*
|
||||
* Initialize the udev backend
|
||||
*/
|
||||
let context = ::smithay::udev::Context::new().map_err(|_| ())?;
|
||||
let seat = session.seat();
|
||||
|
||||
let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default();
|
||||
|
||||
let bytes = include_bytes!("../resources/cursor2.rgba");
|
||||
let mut udev_backend = UdevBackend::new(
|
||||
event_loop.token(),
|
||||
&context,
|
||||
session.clone(),
|
||||
UdevHandlerImpl {
|
||||
compositor_token,
|
||||
active_egl_context,
|
||||
backends: HashMap::new(),
|
||||
display: display.clone(),
|
||||
primary_gpu,
|
||||
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(),
|
||||
).map_err(|_| ())?;
|
||||
|
||||
let udev_session_id = notifier.register(&mut udev_backend);
|
||||
|
||||
let (mut w_seat, _) = Seat::new(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
session.seat().into(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let pointer = w_seat.add_pointer();
|
||||
let keyboard = w_seat
|
||||
.add_keyboard("", "", "", None, 1000, 500)
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
let (output, _output_global) = Output::new(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
"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
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
/*
|
||||
* Initialize libinput backend
|
||||
*/
|
||||
let mut libinput_context =
|
||||
Libinput::new_from_udev::<LibinputSessionInterface<AutoSession>>(session.clone().into(), &context);
|
||||
let libinput_session_id = notifier.register(&mut libinput_context);
|
||||
libinput_context.udev_assign_seat(&seat).unwrap();
|
||||
let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone());
|
||||
libinput_backend.set_handler(AnvilInputHandler::new_with_session(
|
||||
log.clone(),
|
||||
pointer,
|
||||
keyboard,
|
||||
window_map.clone(),
|
||||
(w, h),
|
||||
running.clone(),
|
||||
pointer_location,
|
||||
session,
|
||||
));
|
||||
let libinput_event_source = libinput_bind(libinput_backend, event_loop.token())
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
let session_event_source = auto_session_bind(notifier, &event_loop.token())
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
let udev_event_source = udev_backend_bind(&event_loop.token(), udev_backend)
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
while running.load(Ordering::SeqCst) {
|
||||
if let Err(_) = event_loop.dispatch(Some(16)) {
|
||||
running.store(false, Ordering::SeqCst);
|
||||
} else {
|
||||
display.borrow_mut().flush_clients();
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
let mut notifier = session_event_source.unbind();
|
||||
notifier.unregister(udev_session_id);
|
||||
notifier.unregister(libinput_session_id);
|
||||
|
||||
libinput_event_source.remove();
|
||||
|
||||
// destroy the udev backend freeing the drm devices
|
||||
//
|
||||
// udev_event_source.remove() returns a Box<Implementation<..>>, downcast_impl
|
||||
// allows us to cast it back to its original type, storing it back into its original
|
||||
// variable to simplify type inference.
|
||||
udev_backend = *(downcast_impl(udev_event_source.remove()).unwrap_or_else(|_| unreachable!()));
|
||||
udev_backend.close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct UdevHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
|
||||
display: Rc<RefCell<Display>>,
|
||||
primary_gpu: Option<PathBuf>,
|
||||
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(
|
||||
&self,
|
||||
device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
|
||||
// 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 backends = HashMap::new();
|
||||
|
||||
// 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 !backends.contains_key(&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 = GliumDrawer::from(
|
||||
device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// create cursor
|
||||
renderer
|
||||
.borrow()
|
||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||
.unwrap();
|
||||
|
||||
// render first frame
|
||||
{
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
backends.insert(crtc, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backends
|
||||
}
|
||||
}
|
||||
|
||||
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
|
||||
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl> {
|
||||
// init hardware acceleration on the primary gpu.
|
||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok();
|
||||
}
|
||||
|
||||
let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
|
||||
self.backends.insert(device.device_id(), backends.clone());
|
||||
|
||||
Some(DrmHandlerImpl {
|
||||
compositor_token: self.compositor_token.clone(),
|
||||
backends,
|
||||
window_map: self.window_map.clone(),
|
||||
pointer_location: self.pointer_location.clone(),
|
||||
logger: self.logger.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
//quick and dirt, just re-init all backends
|
||||
let backends = self.backends.get(&device.device_id()).unwrap();
|
||||
*backends.borrow_mut() = self.scan_connectors(device);
|
||||
}
|
||||
|
||||
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
// drop the backends on this side
|
||||
self.backends.remove(&device.device_id());
|
||||
|
||||
// don't use hardware acceleration anymore, if this was the primary gpu
|
||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, error: IoError) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||
{
|
||||
let (x, y) = *self.pointer_location.borrow();
|
||||
let _ = drawer
|
||||
.borrow()
|
||||
.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32);
|
||||
}
|
||||
|
||||
drawer.draw_windows(
|
||||
&*self.window_map.borrow(),
|
||||
self.compositor_token,
|
||||
&self.logger,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, SurfaceAttri
|
|||
use smithay::wayland::compositor::roles::Role;
|
||||
use smithay::wayland::shell::xdg::{ToplevelSurface, XdgSurfaceRole};
|
||||
use smithay::wayland::shell::legacy::{ShellSurface, ShellSurfaceRole};
|
||||
use wayland_server::Resource;
|
||||
use wayland_server::protocol::wl_surface;
|
||||
use smithay::wayland_server::Resource;
|
||||
use smithay::wayland_server::protocol::wl_surface;
|
||||
|
||||
pub enum Kind<U, R, SD, D> {
|
||||
Xdg(ToplevelSurface<U, R, SD>),
|
|
@ -0,0 +1,109 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use smithay::wayland::shm::init_shm_global;
|
||||
use smithay::wayland::seat::Seat;
|
||||
use smithay::wayland::output::{Mode, Output, PhysicalProperties};
|
||||
use smithay::backend::input::InputBackend;
|
||||
use smithay::backend::winit;
|
||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||
use smithay::backend::graphics::egl::wayland::EGLWaylandExtensions;
|
||||
use smithay::wayland_server::{Display, EventLoop};
|
||||
use smithay::wayland_server::protocol::wl_output;
|
||||
|
||||
use slog::Logger;
|
||||
|
||||
use glium_drawer::GliumDrawer;
|
||||
use shell::init_shell;
|
||||
use input_handler::AnvilInputHandler;
|
||||
|
||||
pub fn run_winit(display: &mut Display, event_loop: &mut EventLoop, log: Logger) -> Result<(), ()> {
|
||||
let (renderer, mut input) = winit::init(log.clone()).map_err(|_| ())?;
|
||||
|
||||
let egl_display = Rc::new(RefCell::new(
|
||||
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||
info!(log, "EGL hardware-acceleration enabled");
|
||||
Some(egl_display)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
));
|
||||
|
||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||
let drawer = GliumDrawer::from(renderer);
|
||||
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
info!(log, "Listening on wayland socket"; "name" => name.clone());
|
||||
::std::env::set_var("WAYLAND_DISPLAY", name);
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
|
||||
/*
|
||||
* Initialize the globals
|
||||
*/
|
||||
|
||||
init_shm_global(display, event_loop.token(), vec![], log.clone());
|
||||
|
||||
let (compositor_token, _, _, window_map) =
|
||||
init_shell(display, event_loop.token(), log.clone(), egl_display);
|
||||
|
||||
let (mut seat, _) = Seat::new(display, event_loop.token(), "winit".into(), log.clone());
|
||||
|
||||
let pointer = seat.add_pointer();
|
||||
let keyboard = seat.add_keyboard("", "fr", "oss", None, 1000, 500)
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
let (output, _) = Output::new(
|
||||
display,
|
||||
event_loop.token(),
|
||||
"Winit".into(),
|
||||
PhysicalProperties {
|
||||
width: 0,
|
||||
height: 0,
|
||||
subpixel: wl_output::Subpixel::Unknown,
|
||||
maker: "Smithay".into(),
|
||||
model: "Winit".into(),
|
||||
},
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
input.set_handler(AnvilInputHandler::new(
|
||||
log.clone(),
|
||||
pointer,
|
||||
keyboard,
|
||||
window_map.clone(),
|
||||
(0, 0),
|
||||
running.clone(),
|
||||
Rc::new(RefCell::new((0.0, 0.0))),
|
||||
));
|
||||
|
||||
info!(log, "Initialization completed, starting the main loop.");
|
||||
|
||||
loop {
|
||||
input.dispatch_new_events().unwrap();
|
||||
|
||||
drawer.draw_windows(&*window_map.borrow(), compositor_token, &log);
|
||||
|
||||
event_loop.dispatch(Some(16)).unwrap();
|
||||
display.flush_clients();
|
||||
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
256
examples/drm.rs
256
examples/drm.rs
|
@ -1,256 +0,0 @@
|
|||
extern crate drm;
|
||||
#[macro_use]
|
||||
extern crate glium;
|
||||
extern crate rand;
|
||||
#[macro_use(define_roles)]
|
||||
extern crate smithay;
|
||||
extern crate wayland_server;
|
||||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_async;
|
||||
extern crate slog_term;
|
||||
|
||||
mod helpers;
|
||||
|
||||
use drm::Device as BasicDevice;
|
||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
use drm::control::crtc;
|
||||
use drm::control::encoder::Info as EncoderInfo;
|
||||
use drm::result::Error as DrmError;
|
||||
use glium::{Blend, Surface};
|
||||
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
||||
use slog::{Drain, Logger};
|
||||
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
|
||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions, Format};
|
||||
use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction};
|
||||
use smithay::wayland::compositor::roles::Role;
|
||||
use smithay::wayland::shm::init_shm_global;
|
||||
use std::cell::RefCell;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Card(File);
|
||||
|
||||
impl AsRawFd for Card {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicDevice for Card {}
|
||||
impl ControlDevice for Card {}
|
||||
|
||||
fn main() {
|
||||
// A logger facility, here we use the terminal for this example
|
||||
let log = Logger::root(
|
||||
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
||||
o!(),
|
||||
);
|
||||
|
||||
// Initialize the wayland server
|
||||
let (mut display, mut event_loop) = wayland_server::Display::new();
|
||||
|
||||
/*
|
||||
* Initialize the drm backend
|
||||
*/
|
||||
// "Find" a suitable drm device
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.write(true);
|
||||
let mut device = DrmDevice::new(
|
||||
Card(options.clone().open("/dev/dri/card0").unwrap()),
|
||||
log.clone(),
|
||||
).unwrap();
|
||||
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = device.resource_handles().unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_info = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| ConnectorInfo::load_from_device(&device, *conn).unwrap())
|
||||
.find(|conn| conn.connection_state() == ConnectorState::Connected)
|
||||
.unwrap();
|
||||
|
||||
// Use the first encoder
|
||||
let encoder_info = EncoderInfo::load_from_device(&device, connector_info.encoders()[0]).unwrap();
|
||||
|
||||
// use the connected crtc if any
|
||||
let crtc = encoder_info.current_crtc()
|
||||
// or use the first one that is compatible with the encoder
|
||||
.unwrap_or_else(||
|
||||
*res_handles.filter_crtcs(encoder_info.possible_crtcs())
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap());
|
||||
|
||||
// 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.)
|
||||
|
||||
// Initialize the hardware backend
|
||||
let renderer = GliumDrawer::from(
|
||||
device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap(),
|
||||
);
|
||||
{
|
||||
/*
|
||||
* Initialize glium
|
||||
*/
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
let egl_display = Rc::new(RefCell::new(
|
||||
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||
info!(log, "EGL hardware-acceleration enabled");
|
||||
Some(egl_display)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
));
|
||||
|
||||
/*
|
||||
* Initialize the globals
|
||||
*/
|
||||
|
||||
init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
|
||||
|
||||
let (compositor_token, _, _, window_map) =
|
||||
init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
|
||||
|
||||
/*
|
||||
* Add a listening socket:
|
||||
*/
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
println!("Listening on socket: {}", name);
|
||||
|
||||
/*
|
||||
* Register the DrmDevice on the EventLoop
|
||||
*/
|
||||
let _source = drm_device_bind(
|
||||
&event_loop.token(),
|
||||
device,
|
||||
DrmHandlerImpl {
|
||||
compositor_token,
|
||||
window_map: window_map.clone(),
|
||||
drawer: renderer,
|
||||
logger: log,
|
||||
},
|
||||
).map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
event_loop.dispatch(Some(16)).unwrap();
|
||||
display.flush_clients();
|
||||
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
drawer: GliumDrawer<DrmBackend<Card>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<Card> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<Card>,
|
||||
_crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
let mut frame = self.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 = self.drawer.borrow().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)| {
|
||||
// there is actually something to draw !
|
||||
if attributes.user_data.texture.is_none() {
|
||||
let mut remove = false;
|
||||
match attributes.user_data.buffer {
|
||||
Some(Buffer::Egl { ref images }) => {
|
||||
match images.format {
|
||||
Format::RGB | Format::RGBA => {
|
||||
attributes.user_data.texture =
|
||||
self.drawer.texture_from_egl(&images);
|
||||
}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.texture = None;
|
||||
remove = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(Buffer::Shm { ref data, ref size }) => {
|
||||
attributes.user_data.texture =
|
||||
Some(self.drawer.texture_from_mem(data, *size));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if remove {
|
||||
attributes.user_data.buffer = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref texture) = attributes.user_data.texture {
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
}
|
||||
info!(self.logger, "Render window");
|
||||
self.drawer.render_texture(
|
||||
&mut frame,
|
||||
texture,
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => images.y_inverted,
|
||||
Buffer::Shm { .. } => false,
|
||||
},
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => (images.width, images.height),
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
},
|
||||
(x, y),
|
||||
screen_dimensions,
|
||||
Blend::alpha_blending(),
|
||||
);
|
||||
TraversalAction::DoChildren((x, y))
|
||||
} else {
|
||||
// we are not display, so our children are neither
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
fn error(&mut self, _device: &mut DrmDevice<Card>, error: DrmError) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
mod glium;
|
||||
mod implementations;
|
||||
mod window_map;
|
||||
|
||||
pub use self::glium::GliumDrawer;
|
||||
pub use self::implementations::*;
|
||||
pub use self::window_map::{Kind as SurfaceKind, WindowMap};
|
654
examples/udev.rs
654
examples/udev.rs
|
@ -1,654 +0,0 @@
|
|||
extern crate drm;
|
||||
#[macro_use]
|
||||
extern crate glium;
|
||||
extern crate image;
|
||||
extern crate input as libinput;
|
||||
extern crate rand;
|
||||
#[macro_use(define_roles)]
|
||||
extern crate smithay;
|
||||
extern crate udev;
|
||||
extern crate wayland_server;
|
||||
extern crate xkbcommon;
|
||||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_async;
|
||||
extern crate slog_term;
|
||||
|
||||
mod helpers;
|
||||
|
||||
use drm::control::{Device as ControlDevice, ResourceInfo};
|
||||
use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
|
||||
use drm::control::crtc;
|
||||
use drm::control::encoder::Info as EncoderInfo;
|
||||
use drm::result::Error as DrmError;
|
||||
use glium::{Blend, Surface};
|
||||
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap, Roles, SurfaceData};
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use libinput::{event, Device as LibinputDevice, Libinput};
|
||||
use libinput::event::keyboard::KeyboardEventTrait;
|
||||
use slog::{Drain, Logger};
|
||||
use smithay::backend::drm::{DevPath, DrmBackend, DrmDevice, DrmHandler};
|
||||
use smithay::backend::graphics::GraphicsBackend;
|
||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||
use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLWaylandExtensions, Format};
|
||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
|
||||
PointerAxisEvent, PointerButtonEvent};
|
||||
use smithay::backend::libinput::{libinput_bind, LibinputInputBackend, LibinputSessionInterface};
|
||||
use smithay::backend::session::{Session, SessionNotifier};
|
||||
use smithay::backend::session::auto::{auto_session_bind, AutoSession};
|
||||
use smithay::backend::udev::{primary_gpu, udev_backend_bind, SessionFdDrmDevice, UdevBackend, UdevHandler};
|
||||
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::shm::init_shm_global;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::Error as IoError;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
use wayland_server::Display;
|
||||
use wayland_server::commons::downcast_impl;
|
||||
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||
use xkbcommon::xkb::keysyms as xkb;
|
||||
|
||||
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,
|
||||
session: AutoSession,
|
||||
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();
|
||||
|
||||
// we cannot borrow `self` into the closure, because we need self.keyboard.
|
||||
// but rust does not borrow all fields separately, so we need to do that manually...
|
||||
let running = &self.running;
|
||||
let mut session = &mut self.session;
|
||||
let log = &self.log;
|
||||
let time = Event::time(&evt);
|
||||
self.keyboard
|
||||
.input(keycode, state, serial, time, move |modifiers, keysym| {
|
||||
debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), "keysym" => xkbcommon::xkb::keysym_get_name(keysym));
|
||||
if modifiers.ctrl && modifiers.alt && keysym == xkb::KEY_BackSpace
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
info!(log, "Stopping example using Ctrl+Alt+Backspace");
|
||||
running.store(false, Ordering::SeqCst);
|
||||
false
|
||||
} else if modifiers.logo && keysym == xkb::KEY_q {
|
||||
info!(log, "Stopping example using Logo+Q");
|
||||
running.store(false, Ordering::SeqCst);
|
||||
false
|
||||
} else if modifiers.ctrl && modifiers.alt && keysym >= xkb::KEY_XF86Switch_VT_1
|
||||
&& keysym <= xkb::KEY_XF86Switch_VT_12
|
||||
&& state == KeyState::Pressed
|
||||
{
|
||||
let vt = (keysym - xkb::KEY_XF86Switch_VT_1 + 1) as i32;
|
||||
info!(log, "Trying to switch to vt {}", vt);
|
||||
if let Err(err) = session.change_vt(vt) {
|
||||
error!(log, "Error switching to vt {}: {}", vt, err);
|
||||
}
|
||||
false
|
||||
} else if modifiers.logo && keysym == xkb::KEY_Return && state == KeyState::Pressed {
|
||||
info!(log, "Launching terminal");
|
||||
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: event::pointer::PointerAxisEvent) {
|
||||
let source = match evt.source() {
|
||||
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
|
||||
input::AxisSource::Finger => wayland_server::protocol::wl_pointer::AxisSource::Finger,
|
||||
input::AxisSource::Wheel | input::AxisSource::WheelTilt => {
|
||||
wayland_server::protocol::wl_pointer::AxisSource::Wheel
|
||||
}
|
||||
};
|
||||
let horizontal_amount = evt.amount(&input::Axis::Horizontal)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0);
|
||||
let vertical_amount = evt.amount(&input::Axis::Vertical)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0);
|
||||
let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal);
|
||||
let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);
|
||||
|
||||
{
|
||||
let mut event = self.pointer.axis();
|
||||
event.source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
event.value(
|
||||
wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||
horizontal_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
event.discrete(
|
||||
wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||
discrete as i32,
|
||||
);
|
||||
}
|
||||
} else if source == wayland_server::protocol::wl_pointer::AxisSource::Finger {
|
||||
event.stop(
|
||||
wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||
evt.time(),
|
||||
);
|
||||
}
|
||||
if vertical_amount != 0.0 {
|
||||
event.value(
|
||||
wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||
vertical_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
event.discrete(
|
||||
wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||
discrete as i32,
|
||||
);
|
||||
}
|
||||
} else if source == wayland_server::protocol::wl_pointer::AxisSource::Finger {
|
||||
event.stop(
|
||||
wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||
evt.time(),
|
||||
);
|
||||
}
|
||||
event.done();
|
||||
}
|
||||
}
|
||||
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() {
|
||||
let active_egl_context = Rc::new(RefCell::new(None));
|
||||
|
||||
// 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::Display::new();
|
||||
|
||||
/*
|
||||
* Add a listening socket
|
||||
*/
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
println!("Listening on socket: {}", name);
|
||||
env::set_var("WAYLAND_DISPLAY", name);
|
||||
let display = Rc::new(RefCell::new(display));
|
||||
|
||||
/*
|
||||
* Initialize the compositor
|
||||
*/
|
||||
init_shm_global(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
vec![],
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let (compositor_token, _, _, window_map) = init_shell(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
log.clone(),
|
||||
active_egl_context.clone(),
|
||||
);
|
||||
|
||||
/*
|
||||
* Initialize session
|
||||
*/
|
||||
let (session, mut notifier) = AutoSession::new(log.clone()).unwrap();
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let pointer_location = Rc::new(RefCell::new((0.0, 0.0)));
|
||||
|
||||
/*
|
||||
* Initialize the udev backend
|
||||
*/
|
||||
let context = udev::Context::new().unwrap();
|
||||
let seat = session.seat();
|
||||
|
||||
let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default();
|
||||
|
||||
let bytes = include_bytes!("resources/cursor2.rgba");
|
||||
let mut udev_backend = UdevBackend::new(
|
||||
event_loop.token(),
|
||||
&context,
|
||||
session.clone(),
|
||||
UdevHandlerImpl {
|
||||
compositor_token,
|
||||
active_egl_context,
|
||||
backends: HashMap::new(),
|
||||
display: display.clone(),
|
||||
primary_gpu,
|
||||
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(&mut udev_backend);
|
||||
|
||||
let (mut w_seat, _) = Seat::new(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
session.seat().into(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let pointer = w_seat.add_pointer();
|
||||
let keyboard = w_seat
|
||||
.add_keyboard("", "", "", None, 1000, 500)
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
let (output, _output_global) = Output::new(
|
||||
&mut display.borrow_mut(),
|
||||
event_loop.token(),
|
||||
"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
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
/*
|
||||
* Initialize libinput backend
|
||||
*/
|
||||
let mut libinput_context =
|
||||
Libinput::new_from_udev::<LibinputSessionInterface<AutoSession>>(session.clone().into(), &context);
|
||||
let libinput_session_id = notifier.register(&mut libinput_context);
|
||||
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,
|
||||
session: session,
|
||||
running: running.clone(),
|
||||
});
|
||||
let libinput_event_source = libinput_bind(libinput_backend, event_loop.token())
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
let session_event_source = auto_session_bind(notifier, &event_loop.token())
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
let udev_event_source = udev_backend_bind(&event_loop.token(), udev_backend)
|
||||
.map_err(|(err, _)| err)
|
||||
.unwrap();
|
||||
|
||||
while running.load(Ordering::SeqCst) {
|
||||
if let Err(_) = event_loop.dispatch(Some(16)) {
|
||||
running.store(false, Ordering::SeqCst);
|
||||
} else {
|
||||
display.borrow_mut().flush_clients();
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
||||
|
||||
println!("Bye Bye");
|
||||
|
||||
let mut notifier = session_event_source.unbind();
|
||||
notifier.unregister(udev_session_id);
|
||||
notifier.unregister(libinput_session_id);
|
||||
|
||||
libinput_event_source.remove();
|
||||
|
||||
// destroy the udev backend freeing the drm devices
|
||||
//
|
||||
// udev_event_source.remove() returns a Box<Implementation<..>>, downcast_impl
|
||||
// allows us to cast it back to its original type, storing it back into its original
|
||||
// variable to simplify type inference.
|
||||
udev_backend = *(downcast_impl(udev_event_source.remove()).unwrap_or_else(|_| unreachable!()));
|
||||
udev_backend.close();
|
||||
}
|
||||
|
||||
struct UdevHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
active_egl_context: Rc<RefCell<Option<EGLDisplay>>>,
|
||||
backends: HashMap<u64, Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>>,
|
||||
display: Rc<RefCell<Display>>,
|
||||
primary_gpu: Option<PathBuf>,
|
||||
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(
|
||||
&self,
|
||||
device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
) -> HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>> {
|
||||
// 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 backends = HashMap::new();
|
||||
|
||||
// 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 !backends.contains_key(&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 = GliumDrawer::from(
|
||||
device
|
||||
.create_backend(crtc, mode, vec![connector_info.handle()])
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// create cursor
|
||||
renderer
|
||||
.borrow()
|
||||
.set_cursor_representation(&self.pointer_image, (2, 2))
|
||||
.unwrap();
|
||||
|
||||
// render first frame
|
||||
{
|
||||
let mut frame = renderer.draw();
|
||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
|
||||
backends.insert(crtc, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backends
|
||||
}
|
||||
}
|
||||
|
||||
impl UdevHandler<DrmHandlerImpl> for UdevHandlerImpl {
|
||||
fn device_added(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) -> Option<DrmHandlerImpl> {
|
||||
// init hardware acceleration on the primary gpu.
|
||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok();
|
||||
}
|
||||
|
||||
let backends = Rc::new(RefCell::new(self.scan_connectors(device)));
|
||||
self.backends.insert(device.device_id(), backends.clone());
|
||||
|
||||
Some(DrmHandlerImpl {
|
||||
compositor_token: self.compositor_token.clone(),
|
||||
backends,
|
||||
window_map: self.window_map.clone(),
|
||||
pointer_location: self.pointer_location.clone(),
|
||||
logger: self.logger.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn device_changed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
//quick and dirt, just re-init all backends
|
||||
let backends = self.backends.get(&device.device_id()).unwrap();
|
||||
*backends.borrow_mut() = self.scan_connectors(device);
|
||||
}
|
||||
|
||||
fn device_removed(&mut self, device: &mut DrmDevice<SessionFdDrmDevice>) {
|
||||
// drop the backends on this side
|
||||
self.backends.remove(&device.device_id());
|
||||
|
||||
// don't use hardware acceleration anymore, if this was the primary gpu
|
||||
if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu {
|
||||
*self.active_egl_context.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, error: IoError) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
compositor_token: CompositorToken<SurfaceData, Roles>,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, GliumDrawer<DrmBackend<SessionFdDrmDevice>>>>>,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: Rc<RefCell<(f64, f64)>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl DrmHandler<SessionFdDrmDevice> for DrmHandlerImpl {
|
||||
fn ready(
|
||||
&mut self,
|
||||
_device: &mut DrmDevice<SessionFdDrmDevice>,
|
||||
crtc: crtc::Handle,
|
||||
_frame: u32,
|
||||
_duration: Duration,
|
||||
) {
|
||||
if let Some(drawer) = self.backends.borrow().get(&crtc) {
|
||||
{
|
||||
let (x, y) = *self.pointer_location.borrow();
|
||||
let _ = drawer
|
||||
.borrow()
|
||||
.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.borrow().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)| {
|
||||
// there is actually something to draw !
|
||||
if attributes.user_data.texture.is_none() {
|
||||
let mut remove = false;
|
||||
match attributes.user_data.buffer {
|
||||
Some(Buffer::Egl { ref images }) => {
|
||||
match images.format {
|
||||
Format::RGB | Format::RGBA => {
|
||||
attributes.user_data.texture =
|
||||
drawer.texture_from_egl(&images);
|
||||
}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.texture = None;
|
||||
remove = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(Buffer::Shm { ref data, ref size }) => {
|
||||
attributes.user_data.texture =
|
||||
Some(drawer.texture_from_mem(data, *size));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if remove {
|
||||
attributes.user_data.buffer = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref texture) = attributes.user_data.texture {
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
}
|
||||
info!(self.logger, "Render window");
|
||||
drawer.render_texture(
|
||||
&mut frame,
|
||||
texture,
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => images.y_inverted,
|
||||
Buffer::Shm { .. } => false,
|
||||
},
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => {
|
||||
(images.width, images.height)
|
||||
}
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
},
|
||||
(x, y),
|
||||
screen_dimensions,
|
||||
Blend::alpha_blending(),
|
||||
);
|
||||
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(&mut self, _device: &mut DrmDevice<SessionFdDrmDevice>, error: DrmError) {
|
||||
error!(self.logger, "{:?}", error);
|
||||
}
|
||||
}
|
|
@ -1,352 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate glium;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_async;
|
||||
extern crate slog_term;
|
||||
#[macro_use(define_roles)]
|
||||
extern crate smithay;
|
||||
extern crate wayland_server;
|
||||
|
||||
mod helpers;
|
||||
|
||||
use glium::Surface;
|
||||
use helpers::{init_shell, Buffer, GliumDrawer, MyWindowMap};
|
||||
use slog::{Drain, Logger};
|
||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||
use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, Format};
|
||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
||||
PointerButtonEvent, PointerMotionAbsoluteEvent};
|
||||
use smithay::backend::winit;
|
||||
use smithay::wayland::compositor::{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::shm::init_shm_global;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wayland_server::Display;
|
||||
use wayland_server::protocol::{wl_output, wl_pointer};
|
||||
|
||||
struct WinitInputHandler {
|
||||
log: Logger,
|
||||
pointer: PointerHandle,
|
||||
keyboard: KeyboardHandle,
|
||||
window_map: Rc<RefCell<MyWindowMap>>,
|
||||
pointer_location: (f64, f64),
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
impl WinitInputHandler {
|
||||
fn next_serial(&mut self) -> u32 {
|
||||
self.serial += 1;
|
||||
self.serial
|
||||
}
|
||||
}
|
||||
|
||||
impl InputHandler<winit::WinitInputBackend> for WinitInputHandler {
|
||||
fn on_seat_created(&mut self, _: &input::Seat) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
fn on_seat_destroyed(&mut self, _: &input::Seat) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
fn on_seat_changed(&mut self, _: &input::Seat) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
fn on_keyboard_key(&mut self, _: &input::Seat, evt: winit::WinitKeyboardInputEvent) {
|
||||
let keycode = evt.key_code();
|
||||
let state = evt.state();
|
||||
debug!(self.log, "key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||
let serial = self.next_serial();
|
||||
self.keyboard
|
||||
.input(keycode, state, serial, evt.time(), |_, _| true);
|
||||
}
|
||||
fn on_pointer_move(&mut self, _: &input::Seat, _: input::UnusedEvent) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
fn on_pointer_move_absolute(&mut self, _: &input::Seat, evt: winit::WinitMouseMovedEvent) {
|
||||
// on winit, mouse events are already in pixel coordinates
|
||||
let (x, y) = evt.position();
|
||||
self.pointer_location = (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: winit::WinitMouseInputEvent) {
|
||||
let serial = self.next_serial();
|
||||
let button = match evt.button() {
|
||||
input::MouseButton::Left => 0x110,
|
||||
input::MouseButton::Right => 0x111,
|
||||
input::MouseButton::Middle => 0x112,
|
||||
input::MouseButton::Other(b) => b as u32,
|
||||
};
|
||||
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);
|
||||
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: winit::WinitMouseWheelEvent) {
|
||||
let source = match evt.source() {
|
||||
input::AxisSource::Continuous => wayland_server::protocol::wl_pointer::AxisSource::Continuous,
|
||||
input::AxisSource::Wheel => wayland_server::protocol::wl_pointer::AxisSource::Wheel,
|
||||
_ => unreachable!(), //winit does not have more specific sources
|
||||
};
|
||||
let horizontal_amount = evt.amount(&input::Axis::Horizontal)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Horizontal).unwrap() * 3.0);
|
||||
let vertical_amount = evt.amount(&input::Axis::Vertical)
|
||||
.unwrap_or_else(|| evt.amount_discrete(&input::Axis::Vertical).unwrap() * 3.0);
|
||||
let horizontal_amount_discrete = evt.amount_discrete(&input::Axis::Horizontal);
|
||||
let vertical_amount_discrete = evt.amount_discrete(&input::Axis::Vertical);
|
||||
|
||||
{
|
||||
let mut event = self.pointer.axis();
|
||||
event.source(source);
|
||||
if horizontal_amount != 0.0 {
|
||||
event.value(
|
||||
wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||
horizontal_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
event.discrete(
|
||||
wayland_server::protocol::wl_pointer::Axis::HorizontalScroll,
|
||||
discrete as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
if vertical_amount != 0.0 {
|
||||
event.value(
|
||||
wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||
vertical_amount,
|
||||
evt.time(),
|
||||
);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
event.discrete(
|
||||
wayland_server::protocol::wl_pointer::Axis::VerticalScroll,
|
||||
discrete as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
event.done();
|
||||
}
|
||||
}
|
||||
fn on_touch_down(&mut self, _: &input::Seat, _: winit::WinitTouchStartedEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_motion(&mut self, _: &input::Seat, _: winit::WinitTouchMovedEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_up(&mut self, _: &input::Seat, _: winit::WinitTouchEndedEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_cancel(&mut self, _: &input::Seat, _: winit::WinitTouchCancelledEvent) {
|
||||
/* not done in this example */
|
||||
}
|
||||
fn on_touch_frame(&mut self, _: &input::Seat, _: input::UnusedEvent) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
fn on_input_config_changed(&mut self, _: &mut ()) {
|
||||
/* never happens with winit */
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// A logger facility, here we use the terminal for this example
|
||||
let log = Logger::root(
|
||||
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
||||
o!(),
|
||||
);
|
||||
|
||||
// Initialize a simple backend for testing
|
||||
let (renderer, mut input) = winit::init(log.clone()).unwrap();
|
||||
|
||||
let (mut display, mut event_loop) = wayland_server::Display::new();
|
||||
|
||||
let egl_display = Rc::new(RefCell::new(
|
||||
if let Ok(egl_display) = renderer.bind_wl_display(&display) {
|
||||
info!(log, "EGL hardware-acceleration enabled");
|
||||
Some(egl_display)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
));
|
||||
|
||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
||||
let drawer = GliumDrawer::from(renderer);
|
||||
|
||||
/*
|
||||
* Initialize the globals
|
||||
*/
|
||||
|
||||
init_shm_global(&mut display, event_loop.token(), vec![], log.clone());
|
||||
|
||||
let (compositor_token, _, _, window_map) =
|
||||
init_shell(&mut display, event_loop.token(), log.clone(), egl_display);
|
||||
|
||||
let (mut seat, _) = Seat::new(
|
||||
&mut display,
|
||||
event_loop.token(),
|
||||
"winit".into(),
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
let pointer = seat.add_pointer();
|
||||
let keyboard = seat.add_keyboard("", "fr", "oss", None, 1000, 500)
|
||||
.expect("Failed to initialize the keyboard");
|
||||
|
||||
let (output, _) = Output::new(
|
||||
&mut display,
|
||||
event_loop.token(),
|
||||
"Winit".into(),
|
||||
PhysicalProperties {
|
||||
width: 0,
|
||||
height: 0,
|
||||
subpixel: wl_output::Subpixel::Unknown,
|
||||
maker: "Smithay".into(),
|
||||
model: "Winit".into(),
|
||||
},
|
||||
log.clone(),
|
||||
);
|
||||
|
||||
output.change_current_state(
|
||||
Some(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
output.set_preferred(Mode {
|
||||
width: w as i32,
|
||||
height: h as i32,
|
||||
refresh: 60_000,
|
||||
});
|
||||
|
||||
input.set_handler(WinitInputHandler {
|
||||
log: log.clone(),
|
||||
pointer,
|
||||
keyboard,
|
||||
window_map: window_map.clone(),
|
||||
pointer_location: (0.0, 0.0),
|
||||
serial: 0,
|
||||
});
|
||||
|
||||
/*
|
||||
* Add a listening socket:
|
||||
*/
|
||||
let name = display.add_socket_auto().unwrap().into_string().unwrap();
|
||||
println!("Listening on socket: {}", name);
|
||||
|
||||
loop {
|
||||
input.dispatch_new_events().unwrap();
|
||||
|
||||
let mut frame = drawer.draw();
|
||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||
// redraw the frame, in a simple but inneficient way
|
||||
{
|
||||
let screen_dimensions = drawer.borrow().get_framebuffer_dimensions();
|
||||
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
|
||||
compositor_token
|
||||
.with_surface_tree_upward(
|
||||
wl_surface,
|
||||
initial_place,
|
||||
|_surface, attributes, role, &(mut x, mut y)| {
|
||||
// there is actually something to draw !
|
||||
if attributes.user_data.texture.is_none() {
|
||||
let mut remove = false;
|
||||
match attributes.user_data.buffer {
|
||||
Some(Buffer::Egl { ref images }) => {
|
||||
match images.format {
|
||||
Format::RGB | Format::RGBA => {
|
||||
attributes.user_data.texture =
|
||||
drawer.texture_from_egl(&images);
|
||||
}
|
||||
_ => {
|
||||
// we don't handle the more complex formats here.
|
||||
attributes.user_data.texture = None;
|
||||
remove = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(Buffer::Shm { ref data, ref size }) => {
|
||||
attributes.user_data.texture =
|
||||
Some(drawer.texture_from_mem(data, *size));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if remove {
|
||||
attributes.user_data.buffer = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref texture) = attributes.user_data.texture {
|
||||
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
|
||||
x += subdata.location.0;
|
||||
y += subdata.location.1;
|
||||
}
|
||||
drawer.render_texture(
|
||||
&mut frame,
|
||||
texture,
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => images.y_inverted,
|
||||
Buffer::Shm { .. } => false,
|
||||
},
|
||||
match *attributes.user_data.buffer.as_ref().unwrap() {
|
||||
Buffer::Egl { ref images } => (images.width, images.height),
|
||||
Buffer::Shm { ref size, .. } => *size,
|
||||
},
|
||||
(x, y),
|
||||
screen_dimensions,
|
||||
glium::Blend {
|
||||
color: glium::BlendingFunction::Addition {
|
||||
source: glium::LinearBlendingFactor::One,
|
||||
destination:
|
||||
glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
alpha: glium::BlendingFunction::Addition {
|
||||
source: glium::LinearBlendingFactor::One,
|
||||
destination:
|
||||
glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
TraversalAction::DoChildren((x, y))
|
||||
} else {
|
||||
// we are not display, so our children are neither
|
||||
TraversalAction::SkipChildren
|
||||
}
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
frame.finish().unwrap();
|
||||
|
||||
event_loop.dispatch(Some(16)).unwrap();
|
||||
display.flush_clients();
|
||||
|
||||
window_map.borrow_mut().refresh();
|
||||
}
|
||||
}
|
18
src/lib.rs
18
src/lib.rs
|
@ -7,27 +7,27 @@
|
|||
// `error_chain!` can recurse deeply
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
extern crate image;
|
||||
pub extern crate image;
|
||||
#[cfg_attr(feature = "backend_session", macro_use)]
|
||||
extern crate nix;
|
||||
extern crate tempfile;
|
||||
extern crate wayland_protocols;
|
||||
extern crate wayland_server;
|
||||
pub extern crate wayland_protocols;
|
||||
pub extern crate wayland_server;
|
||||
extern crate wayland_sys;
|
||||
extern crate xkbcommon;
|
||||
|
||||
#[cfg(feature = "dbus")]
|
||||
extern crate dbus;
|
||||
pub extern crate dbus;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
extern crate drm;
|
||||
pub extern crate drm;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
extern crate gbm;
|
||||
pub extern crate gbm;
|
||||
#[cfg(feature = "backend_libinput")]
|
||||
extern crate input;
|
||||
pub extern crate input;
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
extern crate systemd;
|
||||
pub extern crate systemd;
|
||||
#[cfg(feature = "udev")]
|
||||
extern crate udev;
|
||||
pub extern crate udev;
|
||||
#[cfg(feature = "backend_winit")]
|
||||
extern crate wayland_client;
|
||||
#[cfg(feature = "backend_winit")]
|
||||
|
|
|
@ -59,7 +59,7 @@ use std::sync::{Arc, Mutex};
|
|||
mod keyboard;
|
||||
mod pointer;
|
||||
|
||||
pub use self::keyboard::{Error as KeyboardError, KeyboardHandle, ModifiersState};
|
||||
pub use self::keyboard::{keysyms, Error as KeyboardError, KeyboardHandle, Keysym, ModifiersState};
|
||||
pub use self::pointer::{PointerAxisHandle, PointerHandle};
|
||||
use wayland_server::{Display, Global, LoopToken, NewResource, Resource};
|
||||
use wayland_server::protocol::wl_seat;
|
||||
|
|
Loading…
Reference in New Issue