diff --git a/.travis.yml b/.travis.yml index cd24a90..09a0917 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 93f6b2a..e1f1619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml new file mode 100644 index 0000000..c49841b --- /dev/null +++ b/anvil/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "anvil" +version = "0.0.1" +authors = ["Victor Berger ", "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" ] + diff --git a/examples/resources/cursor.rgba b/anvil/resources/cursor.rgba similarity index 100% rename from examples/resources/cursor.rgba rename to anvil/resources/cursor.rgba diff --git a/examples/resources/cursor2.rgba b/anvil/resources/cursor2.rgba similarity index 100% rename from examples/resources/cursor2.rgba rename to anvil/resources/cursor2.rgba diff --git a/examples/helpers/glium.rs b/anvil/src/glium_drawer.rs similarity index 50% rename from examples/helpers/glium.rs rename to anvil/src/glium_drawer.rs index ecb2ec2..30c5e3c 100644 --- a/examples/helpers/glium.rs +++ b/anvil/src/glium_drawer.rs @@ -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 EGLWaylandExtension self.display.bind_wl_display(display) } } + +impl GliumDrawer { + 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::::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); + } + } +} diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs new file mode 100644 index 0000000..28edc94 --- /dev/null +++ b/anvil/src/input_handler.rs @@ -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>, + pointer_location: Rc>, + screen_size: (u32, u32), + serial: u32, + #[cfg(feature = "udev")] + session: Option, + running: Arc, +} + +impl AnvilInputHandler { + pub fn new( + log: Logger, + pointer: PointerHandle, + keyboard: KeyboardHandle, + window_map: Rc>, + screen_size: (u32, u32), + running: Arc, + pointer_location: Rc>, + ) -> 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>, + screen_size: (u32, u32), + running: Arc, + pointer_location: Rc>, + 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 InputHandler 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 + } +} diff --git a/anvil/src/main.rs b/anvil/src/main.rs new file mode 100644 index 0000000..e108950 --- /dev/null +++ b/anvil/src/main.rs @@ -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); + } + } + } +} diff --git a/anvil/src/raw_drm.rs b/anvil/src/raw_drm.rs new file mode 100644 index 0000000..e282a1d --- /dev/null +++ b/anvil/src/raw_drm.rs @@ -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, + window_map: Rc>, + drawer: GliumDrawer>, + logger: ::slog::Logger, +} + +impl DrmHandler for DrmHandlerImpl { + fn ready( + &mut self, + _device: &mut DrmDevice, + _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, error: DrmError) { + panic!("{:?}", error); + } +} diff --git a/examples/helpers/implementations.rs b/anvil/src/shell.rs similarity index 96% rename from examples/helpers/implementations.rs rename to anvil/src/shell.rs index d494bb9..ad5940e 100644 --- a/examples/helpers/implementations.rs +++ b/anvil/src/shell.rs @@ -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, - pub texture: Option, -} - -pub enum Buffer { - Egl { images: EGLImages }, - Shm { data: Vec, size: (u32, u32) }, -} - -fn surface_commit( - surface: &Resource, - token: CompositorToken, - display: &RefCell>, -) { - // 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) -> 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) -> Option<(i32, i32)>>; +pub type MyCompositorToken = CompositorToken; + 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, + pub texture: Option, +} + +pub enum Buffer { + Egl { images: EGLImages }, + Shm { data: Vec, size: (u32, u32) }, +} + +fn surface_commit( + surface: &Resource, + token: CompositorToken, + display: &RefCell>, +) { + // 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) -> 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)) +} diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs new file mode 100644 index 0000000..1dc710e --- /dev/null +++ b/anvil/src/udev.rs @@ -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::>(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>, 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, + active_egl_context: Rc>>, + backends: HashMap>>>>>, + display: Rc>, + primary_gpu: Option, + window_map: Rc>, + pointer_location: Rc>, + pointer_image: ImageBuffer, Vec>, + logger: ::slog::Logger, +} + +impl UdevHandlerImpl { + pub fn scan_connectors( + &self, + device: &mut DrmDevice, + ) -> HashMap>> { + // 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 = 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::>(); + 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 for UdevHandlerImpl { + fn device_added(&mut self, device: &mut DrmDevice) -> Option { + // 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) { + //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) { + // 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, + backends: Rc>>>>, + window_map: Rc>, + pointer_location: Rc>, + logger: ::slog::Logger, +} + +impl DrmHandler for DrmHandlerImpl { + fn ready( + &mut self, + _device: &mut DrmDevice, + 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, error: DrmError) { + error!(self.logger, "{:?}", error); + } +} diff --git a/examples/helpers/window_map.rs b/anvil/src/window_map.rs similarity index 98% rename from examples/helpers/window_map.rs rename to anvil/src/window_map.rs index af79ca3..e0f6fd3 100644 --- a/examples/helpers/window_map.rs +++ b/anvil/src/window_map.rs @@ -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 { Xdg(ToplevelSurface), diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs new file mode 100644 index 0000000..b737bf3 --- /dev/null +++ b/anvil/src/winit.rs @@ -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(); + } +} diff --git a/examples/drm.rs b/examples/drm.rs deleted file mode 100644 index 7993c72..0000000 --- a/examples/drm.rs +++ /dev/null @@ -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, - window_map: Rc>, - drawer: GliumDrawer>, - logger: ::slog::Logger, -} - -impl DrmHandler for DrmHandlerImpl { - fn ready( - &mut self, - _device: &mut DrmDevice, - _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::::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, error: DrmError) { - panic!("{:?}", error); - } -} diff --git a/examples/helpers/mod.rs b/examples/helpers/mod.rs deleted file mode 100644 index 2747f60..0000000 --- a/examples/helpers/mod.rs +++ /dev/null @@ -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}; diff --git a/examples/udev.rs b/examples/udev.rs deleted file mode 100644 index 36adbaa..0000000 --- a/examples/udev.rs +++ /dev/null @@ -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>, - pointer_location: Rc>, - screen_size: (u32, u32), - serial: u32, - session: AutoSession, - running: Arc, -} - -impl LibinputInputHandler { - fn next_serial(&mut self) -> u32 { - self.serial += 1; - self.serial - } -} - -impl InputHandler 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::>(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>, 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, - active_egl_context: Rc>>, - backends: HashMap>>>>>, - display: Rc>, - primary_gpu: Option, - window_map: Rc>, - pointer_location: Rc>, - pointer_image: ImageBuffer, Vec>, - logger: ::slog::Logger, -} - -impl UdevHandlerImpl { - pub fn scan_connectors( - &self, - device: &mut DrmDevice, - ) -> HashMap>> { - // 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 = 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::>(); - 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 for UdevHandlerImpl { - fn device_added(&mut self, device: &mut DrmDevice) -> Option { - // 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) { - //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) { - // 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, - backends: Rc>>>>, - window_map: Rc>, - pointer_location: Rc>, - logger: ::slog::Logger, -} - -impl DrmHandler for DrmHandlerImpl { - fn ready( - &mut self, - _device: &mut DrmDevice, - 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::::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, error: DrmError) { - error!(self.logger, "{:?}", error); - } -} diff --git a/examples/winit.rs b/examples/winit.rs deleted file mode 100644 index a680b8d..0000000 --- a/examples/winit.rs +++ /dev/null @@ -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>, - pointer_location: (f64, f64), - serial: u32, -} - -impl WinitInputHandler { - fn next_serial(&mut self) -> u32 { - self.serial += 1; - self.serial - } -} - -impl InputHandler 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::::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(); - } -} diff --git a/src/lib.rs b/src/lib.rs index 9366821..28f7707 100644 --- a/src/lib.rs +++ b/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")] diff --git a/src/wayland/seat/mod.rs b/src/wayland/seat/mod.rs index 9684636..d45d229 100644 --- a/src/wayland/seat/mod.rs +++ b/src/wayland/seat/mod.rs @@ -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;