Merge pull request #9 from Drakulix/feature/glutin

First draft of a glutin backend
This commit is contained in:
Victor Berger 2017-03-20 16:16:06 +01:00 committed by GitHub
commit f40bd92e14
12 changed files with 1007 additions and 3 deletions

View File

@ -4,8 +4,8 @@ fn_args_layout = "Visual"
fn_arg_intent = "Tabbed" fn_arg_intent = "Tabbed"
reorder_imports = true reorder_imports = true
reorder_imported_names = true reorder_imported_names = true
report_todo = "Always" report_todo = "Never"
report_fixme = "Always" report_fixme = "Never"
normalize_comments = true normalize_comments = true
use_try_shorthand = true use_try_shorthand = true
max_width = 110 max_width = 110

View File

@ -26,6 +26,8 @@ branches:
before_script: before_script:
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH - export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
- which rustfmt || cargo install rustfmt - which rustfmt || cargo install rustfmt
- which cargo-install-update || cargo install cargo-update
- cargo install-update -a
- pip install 'travis-cargo<0.2' --user - pip install 'travis-cargo<0.2' --user
- mkdir $(pwd)/socket - mkdir $(pwd)/socket
- export XDG_RUNTIME_DIR="$(pwd)/socket" - export XDG_RUNTIME_DIR="$(pwd)/socket"

View File

@ -4,11 +4,18 @@ version = "0.1.0"
authors = ["Victor Berger <victor.berger@thalesgroup.com>"] authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
[dependencies] [dependencies]
wayland-server = "0.8.4" wayland-server = "0.8.6"
nix = "0.7.0" nix = "0.7.0"
glutin = { version = "~0.7.4", optional = true }
glium = { version = "~0.16.0", optional = true }
slog = { version = "~1.5.2", features = ["max_level_trace", "release_max_level_info"] } slog = { version = "~1.5.2", features = ["max_level_trace", "release_max_level_info"] }
slog-stdlog = "~1.1.0" slog-stdlog = "~1.1.0"
clippy = { version = "*", optional = true } clippy = { version = "*", optional = true }
[dev-dependencies] [dev-dependencies]
slog-term = "~1.5" slog-term = "~1.5"
[features]
default = ["backend_glutin", "renderer_glium"]
backend_glutin = ["glutin", "wayland-server/dlopen"]
renderer_glium = ["glium"]

36
examples/simple.rs Normal file
View File

@ -0,0 +1,36 @@
extern crate wayland_server;
extern crate smithay;
use smithay::backend::glutin;
use smithay::backend::input::InputBackend;
use smithay::shm::ShmGlobal;
use wayland_server::protocol::wl_shm;
fn main() {
let (_, mut event_loop) = wayland_server::create_display();
// Insert the ShmGlobal as a handler to your event loop
// Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported.
let handler_id =
event_loop.add_handler_with_init(ShmGlobal::new(vec![],
None /* we don't provide a logger here */));
// Register this handler to advertise a wl_shm global of version 1
let shm_global = event_loop.register_global::<wl_shm::WlShm, ShmGlobal>(handler_id, 1);
// Retrieve the shm token for later use to access the buffers
let shm_token = {
let state = event_loop.state();
state.get_handler::<ShmGlobal>(handler_id).get_token()
};
// Initialize a simple backend for testing
let (mut renderer, mut input) = glutin::init_windowed().unwrap();
// TODO render stuff
// TODO put input handling on the event loop
input.dispatch_new_events().unwrap();
event_loop.run().unwrap();
}

50
src/backend/glium.rs Normal file
View File

@ -0,0 +1,50 @@
use backend::graphics::opengl::{OpenglGraphicsBackend, SwapBuffersError};
use glium::SwapBuffersError as GliumSwapBuffersError;
use glium::backend::Backend;
use std::os::raw::c_void;
impl From<SwapBuffersError> for GliumSwapBuffersError {
fn from(error: SwapBuffersError) -> Self {
match error {
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
}
}
}
pub struct GliumGraphicBackend<T: OpenglGraphicsBackend>(T);
pub trait IntoGlium: OpenglGraphicsBackend + Sized {
fn into_glium(self) -> GliumGraphicBackend<Self>;
}
impl<T: OpenglGraphicsBackend> IntoGlium for T {
fn into_glium(self) -> GliumGraphicBackend<Self> {
GliumGraphicBackend(self)
}
}
unsafe impl<T: OpenglGraphicsBackend> Backend for GliumGraphicBackend<T> {
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
self.0.swap_buffers().map_err(Into::into)
}
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.0.get_proc_address(symbol) as *const c_void
}
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
self.0.get_framebuffer_dimensions()
}
fn is_current(&self) -> bool {
self.0.is_current()
}
unsafe fn make_current(&self) {
self.0.make_current()
}
}

422
src/backend/glutin.rs Normal file
View File

@ -0,0 +1,422 @@
//! Implementation of backend traits for types provided by `glutin`
use backend::{SeatInternal, TouchSlotInternal};
use backend::graphics::opengl::{Api, OpenglGraphicsBackend, PixelFormat, SwapBuffersError};
use backend::input::{Axis, AxisSource, InputBackend, InputHandler, KeyState, MouseButton, MouseButtonState,
Seat, SeatCapabilities, TouchEvent, TouchSlot};
use glutin::{Api as GlutinApi, MouseButton as GlutinMouseButton, PixelFormat as GlutinPixelFormat};
use glutin::{ContextError, CreationError, ElementState, Event, GlContext, HeadlessContext,
HeadlessRendererBuilder, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder};
use nix::c_void;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
/// Create a new `GlutinHeadlessRenderer` which implements the `OpenglRenderer` graphics
/// backend trait
pub fn init_headless_renderer() -> Result<GlutinHeadlessRenderer, CreationError> {
init_headless_renderer_from_builder(HeadlessRendererBuilder::new(1024, 600))
}
/// Create a new `GlutinHeadlessRenderer`, which implements the `OpenglRenderer` graphics
/// backend trait, with a given already configured `HeadlessRendererBuilder` for
/// customization
pub fn init_headless_renderer_from_builder(builder: HeadlessRendererBuilder)
-> Result<GlutinHeadlessRenderer, CreationError> {
let (w, h) = builder.dimensions;
let context = builder.build_strict()?;
Ok(GlutinHeadlessRenderer::new(context, w, h))
}
/// Create a new `GlutinWindowedRenderer`, which implements the `OpenglRenderer` graphics
/// backend trait
pub fn init_windowed_renderer() -> Result<GlutinWindowedRenderer, CreationError> {
init_windowed_renderer_from_builder(WindowBuilder::new())
}
/// Create a new `GlutinWindowedRenderer`, which implements the `OpenglRenderer` graphics
/// backend trait, with a given already configured `WindowBuilder` for customization.
pub fn init_windowed_renderer_from_builder(builder: WindowBuilder)
-> Result<GlutinWindowedRenderer, CreationError> {
let window = Rc::new(builder.build_strict()?);
Ok(GlutinWindowedRenderer::new(window))
}
/// Create a new `glutin` `Window`. Returns a `GlutinWindowedRenderer` implementing
/// the `OpenglRenderer` graphics backend trait and a `GlutinInputBackend` implementing
/// the `InputBackend` trait.
pub fn init_windowed() -> Result<(GlutinWindowedRenderer, GlutinInputBackend), CreationError> {
init_windowed_from_builder(WindowBuilder::new())
}
/// Create a new `glutin` `Window` with a given already configured `WindowBuilder` for
/// customization. Returns a `GlutinWindowedRenderer` implementing
/// the `OpenglRenderer` graphics backend trait and a `GlutinInputBackend` implementing
/// the `InputBackend` trait.
pub fn init_windowed_from_builder(builder: WindowBuilder)
-> Result<(GlutinWindowedRenderer, GlutinInputBackend), CreationError> {
let window = Rc::new(builder.build_strict()?);
Ok((GlutinWindowedRenderer::new(window.clone()), GlutinInputBackend::new(window)))
}
/// Headless Opengl Context created by `glutin`. Implements the `OpenglGraphicsBackend` graphics
/// backend trait.
pub struct GlutinHeadlessRenderer {
context: HeadlessContext,
w: u32,
h: u32,
}
impl GlutinHeadlessRenderer {
fn new(context: HeadlessContext, w: u32, h: u32) -> GlutinHeadlessRenderer {
GlutinHeadlessRenderer {
context: context,
w: w,
h: h,
}
}
}
impl OpenglGraphicsBackend for GlutinHeadlessRenderer {
#[inline]
fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
match self.context.swap_buffers() {
Ok(()) => Ok(()),
Err(ContextError::IoError(e)) => panic!("Error while swapping buffers: {:?}", e),
Err(ContextError::ContextLost) => Err(SwapBuffersError::ContextLost),
}
}
#[inline]
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.context.get_proc_address(symbol) as *const _
}
#[inline]
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
(self.w, self.h)
}
#[inline]
fn is_current(&self) -> bool {
self.context.is_current()
}
#[inline]
unsafe fn make_current(&self) {
self.context.make_current().unwrap();
}
fn get_api(&self) -> Api {
self.context.get_api().into()
}
fn get_pixel_format(&self) -> PixelFormat {
self.context.get_pixel_format().into()
}
}
/// Window with an active Opengl Context created by `glutin`. Implements the
/// `OpenglGraphicsBackend` graphics backend trait.
pub struct GlutinWindowedRenderer {
window: Rc<Window>,
}
impl GlutinWindowedRenderer {
fn new(window: Rc<Window>) -> GlutinWindowedRenderer {
GlutinWindowedRenderer { window: window }
}
}
impl OpenglGraphicsBackend for GlutinWindowedRenderer {
#[inline]
fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
match self.window.swap_buffers() {
Ok(()) => Ok(()),
Err(ContextError::IoError(e)) => panic!("Error while swapping buffers: {:?}", e),
Err(ContextError::ContextLost) => Err(SwapBuffersError::ContextLost),
}
}
#[inline]
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.window.get_proc_address(symbol) as *const _
}
#[inline]
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let (width, height) = self.window.get_inner_size().unwrap_or((800, 600)); // TODO: 800x600 ?
let scale = self.window.hidpi_factor();
((width as f32 * scale) as u32, (height as f32 * scale) as u32)
}
#[inline]
fn is_current(&self) -> bool {
self.window.is_current()
}
#[inline]
unsafe fn make_current(&self) {
self.window.make_current().unwrap();
}
fn get_api(&self) -> Api {
self.window.get_api().into()
}
fn get_pixel_format(&self) -> PixelFormat {
self.window.get_pixel_format().into()
}
}
/// Errors that may happen when driving the event loop of `GlutinInputBackend`
#[derive(Debug)]
pub enum GlutinInputError {
/// The underlying `glutin` `Window` was closed. No further events can be processed.
///
/// See `GlutinInputBackend::process_new_events`.
WindowClosed,
}
impl Error for GlutinInputError {
fn description(&self) -> &str {
match *self {
GlutinInputError::WindowClosed => "Glutin Window was closed",
}
}
}
impl fmt::Display for GlutinInputError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
/// Abstracted event loop of a `glutin` `Window` implementing the `InputBackend` trait
///
/// You need to call `process_new_events` periodically to receive any events.
pub struct GlutinInputBackend {
window: Rc<Window>,
time_counter: u32,
seat: Seat,
input_config: (),
handler: Option<Box<InputHandler<GlutinInputBackend> + 'static>>,
}
impl InputBackend for GlutinInputBackend {
type InputConfig = ();
type EventError = GlutinInputError;
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, mut handler: H) {
if self.handler.is_some() {
self.clear_handler();
}
handler.on_seat_created(&self.seat);
self.handler = Some(Box::new(handler));
}
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>> {
self.handler.as_mut().map(|handler| handler as &mut InputHandler<Self>)
}
fn clear_handler(&mut self) {
if let Some(mut handler) = self.handler.take() {
handler.on_seat_destroyed(&self.seat);
}
}
fn input_config(&mut self) -> &mut Self::InputConfig {
&mut self.input_config
}
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> {
if let Some((win_x, win_y)) = self.window.get_position() {
self.window.set_cursor_position(win_x + x as i32, win_y + y as i32)
} else {
Err(())
}
}
/// Processes new events of the underlying event loop to drive the set `InputHandler`.
///
/// You need to periodically call this function to keep the underlying event loop and
/// `Window` active. Otherwise the window may no respond to user interaction and no
/// input events will be received by a set `InputHandler`.
///
/// Returns an error if the `Window` the window has been closed. Calling
/// `process_new_events` again after the `Window` has been closed is considered an
/// application error and unspecified baviour may occur.
///
/// The linked `GlutinWindowedRenderer` will error with a lost Context and should
/// not be used anymore as well.
fn dispatch_new_events(&mut self) -> Result<(), GlutinInputError> {
for event in self.window.poll_events() {
if let Some(ref mut handler) = self.handler {
match event {
Event::KeyboardInput(state, key_code, _) => {
handler.on_keyboard_key(&self.seat,
self.time_counter,
key_code as u32,
state.into(),
1)
}
Event::MouseMoved(x, y) => {
handler.on_pointer_move(&self.seat, self.time_counter, (x as u32, y as u32))
}
Event::MouseWheel(delta, _) => {
match delta {
MouseScrollDelta::LineDelta(x, y) => {
if x != 0.0 {
handler.on_pointer_scroll(&self.seat,
self.time_counter,
Axis::Horizontal,
AxisSource::Wheel,
x as f64);
}
if y != 0.0 {
handler.on_pointer_scroll(&self.seat,
self.time_counter,
Axis::Vertical,
AxisSource::Wheel,
y as f64);
}
}
MouseScrollDelta::PixelDelta(x, y) => {
if x != 0.0 {
handler.on_pointer_scroll(&self.seat,
self.time_counter,
Axis::Vertical,
AxisSource::Continous,
x as f64);
}
if y != 0.0 {
handler.on_pointer_scroll(&self.seat,
self.time_counter,
Axis::Horizontal,
AxisSource::Continous,
y as f64);
}
}
}
}
Event::MouseInput(state, button) => {
handler.on_pointer_button(&self.seat, self.time_counter, button.into(), state.into())
}
Event::Touch(Touch { phase: TouchPhase::Started, location: (x, y), id }) => {
handler.on_touch(&self.seat,
self.time_counter,
TouchEvent::Down {
slot: Some(TouchSlot::new(id as u32)),
x: x,
y: y,
})
}
Event::Touch(Touch { phase: TouchPhase::Moved, location: (x, y), id }) => {
handler.on_touch(&self.seat,
self.time_counter,
TouchEvent::Motion {
slot: Some(TouchSlot::new(id as u32)),
x: x,
y: y,
})
}
Event::Touch(Touch { phase: TouchPhase::Ended, location: (x, y), id }) => {
handler.on_touch(&self.seat,
self.time_counter,
TouchEvent::Motion {
slot: Some(TouchSlot::new(id as u32)),
x: x,
y: y,
});
handler.on_touch(&self.seat,
self.time_counter,
TouchEvent::Up { slot: Some(TouchSlot::new(id as u32)) });
}
Event::Touch(Touch { phase: TouchPhase::Cancelled, id, .. }) => {
handler.on_touch(&self.seat,
self.time_counter,
TouchEvent::Cancel { slot: Some(TouchSlot::new(id as u32)) })
}
Event::Closed => return Err(GlutinInputError::WindowClosed),
_ => {}
}
self.time_counter += 1;
}
}
Ok(())
}
}
impl GlutinInputBackend {
fn new(window: Rc<Window>) -> GlutinInputBackend {
GlutinInputBackend {
window: window,
time_counter: 0,
seat: Seat::new(0,
SeatCapabilities {
pointer: true,
keyboard: true,
touch: true,
}),
input_config: (),
handler: None,
}
}
}
impl From<GlutinApi> for Api {
fn from(api: GlutinApi) -> Self {
match api {
GlutinApi::OpenGl => Api::OpenGl,
GlutinApi::OpenGlEs => Api::OpenGlEs,
GlutinApi::WebGl => Api::WebGl,
}
}
}
impl From<GlutinPixelFormat> for PixelFormat {
fn from(format: GlutinPixelFormat) -> Self {
PixelFormat {
hardware_accelerated: format.hardware_accelerated,
color_bits: format.color_bits,
alpha_bits: format.alpha_bits,
depth_bits: format.depth_bits,
stencil_bits: format.stencil_bits,
stereoscopy: format.stereoscopy,
double_buffer: format.double_buffer,
multisampling: format.multisampling,
srgb: format.srgb,
}
}
}
impl From<GlutinMouseButton> for MouseButton {
fn from(button: GlutinMouseButton) -> MouseButton {
match button {
GlutinMouseButton::Left => MouseButton::Left,
GlutinMouseButton::Right => MouseButton::Right,
GlutinMouseButton::Middle => MouseButton::Middle,
GlutinMouseButton::Other(num) => MouseButton::Other(num),
}
}
}
impl From<ElementState> for KeyState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => KeyState::Pressed,
ElementState::Released => KeyState::Released,
}
}
}
impl From<ElementState> for MouseButtonState {
fn from(state: ElementState) -> Self {
match state {
ElementState::Pressed => MouseButtonState::Pressed,
ElementState::Released => MouseButtonState::Released,
}
}
}

View File

@ -0,0 +1,6 @@
//! Common traits for various ways to renderer on a given graphics backend.
//!
//! Note: Not every api may be supported by every backend
pub mod software;
pub mod opengl;

View File

@ -0,0 +1,94 @@
//! Common traits and types for opengl rendering on graphics backends
use nix::c_void;
/// Error that can happen when swapping buffers.
#[derive(Debug, Clone)]
pub enum SwapBuffersError {
/// The OpenGL context has been lost and needs to be recreated.
///
/// All the objects associated to it (textures, buffers, programs, etc.)
/// need to be recreated from scratch.
///
/// Operations will have no effect. Functions that read textures, buffers, etc.
/// from OpenGL will return uninitialized data instead.
///
/// A context loss usually happens on mobile devices when the user puts the
/// application on sleep and wakes it up later. However any OpenGL implementation
/// can theoretically lose the context at any time.
ContextLost,
/// The buffers have already been swapped.
///
/// This error can be returned when `swap_buffers` has been called multiple times
/// without any modification in between.
AlreadySwapped,
}
/// All APIs related to OpenGL that you can possibly get
/// through OpenglRenderer implementations
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Api {
/// The classical OpenGL. Available on Windows, Linux, OS/X.
OpenGl,
/// OpenGL embedded system. Available on Linux, Android.
OpenGlEs,
/// OpenGL for the web. Very similar to OpenGL ES.
WebGl,
}
/// Describes the pixel format of the main framebuffer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormat {
/// is the format hardware accelerated
pub hardware_accelerated: bool,
/// number of bits used for colors
pub color_bits: u8,
/// number of bits used for alpha channel
pub alpha_bits: u8,
/// number of bits used for depth channel
pub depth_bits: u8,
/// number of bits used for stencil buffer
pub stencil_bits: u8,
/// is stereoscopy enabled
pub stereoscopy: bool,
/// is double buffering enabled
pub double_buffer: bool,
/// number of samples used for multisampling if enabled
pub multisampling: Option<u16>,
/// is srgb enabled
pub srgb: bool,
}
/// Trait that describes objects that have an OpenGl context
/// and can be used to render upon
pub trait OpenglGraphicsBackend {
/// Swaps buffers at the end of a frame.
fn swap_buffers(&self) -> Result<(), SwapBuffersError>;
/// Returns the address of an OpenGL function.
///
/// Supposes that the context has been made current before this function is called.
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;
/// Returns the dimensions of the window, or screen, etc in points.
///
/// That are the scaled pixels of the underlying graphics backend.
/// For nested compositors this will respect the scaling of the root compositor.
/// For drawing directly onto hardware this unit will be equal to actual pixels.
fn get_framebuffer_dimensions(&self) -> (u32, u32);
/// Returns true if the OpenGL context is the current one in the thread.
fn is_current(&self) -> bool;
/// Makes the OpenGL context the current context in the current thread.
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
unsafe fn make_current(&self);
/// Returns the OpenGL API being used.
fn get_api(&self) -> Api;
/// Returns the pixel format of the main framebuffer of the context.
fn get_pixel_format(&self) -> PixelFormat;
}

View File

@ -0,0 +1,17 @@
//! Common traits and types used for software rendering on graphics backends
use std::error::Error;
use wayland_server::protocol::wl_shm::Format;
/// Trait that describes objects providing a software rendering implementation
pub trait CpuGraphicsBackend<E: Error> {
/// Render a given buffer of a given format at a specified place in the framebuffer
///
/// # Error
/// Returns an error if the buffer size does not match the required amount of pixels
/// for the given size or if the position and size is out of scope of the framebuffer.
fn render(&mut self, buffer: &[u8], format: Format, at: (u32, u32), size: (u32, u32)) -> Result<(), E>;
/// Returns the dimensions of the Framebuffer
fn get_framebuffer_dimensions(&self) -> (u32, u32);
}

329
src/backend/input.rs Normal file
View File

@ -0,0 +1,329 @@
//! Common traits for input backends to receive input from.
use backend::{SeatInternal, TouchSlotInternal};
use std::error::Error;
/// A seat describes a group of input devices and at least one
/// graphics device belonging together.
///
/// By default only one seat exists for most systems and smithay backends
/// however multiseat configurations are possible and should be treated as
/// separated users, all with their own focus, input and cursor available.
///
/// Seats can be checked for equality and hashed for differentiation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Seat {
id: u32,
capabilities: SeatCapabilities,
}
impl SeatInternal for Seat {
fn new(id: u32, capabilities: SeatCapabilities) -> Seat {
Seat {
id: id,
capabilities: capabilities,
}
}
}
impl Seat {
/// Get the currently capabilities of this `Seat`
pub fn capabilities(&self) -> &SeatCapabilities {
&self.capabilities
}
}
/// Describes capabilities a `Seat` has.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SeatCapabilities {
/// `Seat` has a pointer
pub pointer: bool,
/// `Seat` has a keyboard
pub keyboard: bool,
/// `Seat` has a touchscreen
pub touch: bool,
}
/// State of key on a keyboard. Either pressed or released
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum KeyState {
/// Key is released
Released,
/// Key is pressed
Pressed,
}
/// A particular mouse button
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton {
/// Left mouse button
Left,
/// Middle mouse button
Middle,
/// Right mouse button
Right,
/// Other mouse button with index
Other(u8),
}
/// State of a button on a mouse. Either pressed or released
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum MouseButtonState {
/// Button is released
Released,
/// Button is pressed
Pressed,
}
/// Axis when scrolling
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Axis {
/// Vertical axis
Vertical,
/// Horizonal axis
Horizontal,
}
/// Source of an axis when scrolling
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AxisSource {
/// Finger. Mostly used for trackpads.
///
/// Guarantees that a scroll sequence is terminated with a scroll value of 0.
/// A caller may use this information to decide on whether kinetic scrolling should
/// be triggered on this scroll sequence.
///
/// The coordinate system is identical to the
/// cursor movement, i.e. a scroll value of 1 represents the equivalent relative
/// motion of 1.
Finger,
/// Continous scrolling device. Almost identical to `Finger`
///
/// No terminating event is guaranteed (though it may happen).
///
/// The coordinate system is identical to
/// the cursor movement, i.e. a scroll value of 1 represents the equivalent relative
/// motion of 1.
Continous,
/// Scroll wheel.
///
/// No terminating event is guaranteed (though it may happen). Scrolling is in
/// discrete steps. It is up to the caller how to interpret such different step sizes.
Wheel,
/// Scrolling through tilting the scroll wheel.
///
/// No terminating event is guaranteed (though it may happen). Scrolling is in
/// discrete steps. It is up to the caller how to interpret such different step sizes.
WheelTilt,
}
/// Slot of a different touch event.
///
/// Touch events are groubed by slots, usually to identify different
/// fingers on a multi-touch enabled input device. Events should only
/// be interpreted in the context of other events on the same slot.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TouchSlot {
id: u32,
}
impl TouchSlotInternal for TouchSlot {
fn new(id: u32) -> Self {
TouchSlot { id: id }
}
}
/// Touch event
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TouchEvent {
/// The start of an event at a given position (x, y).
///
/// If the device has multi-touch capabilities a slot is given.
Down {
/// `TouchSlot`, if the device has multi-touch capabilities
slot: Option<TouchSlot>,
/// Absolute x-coordinate of the touch position.
x: f64,
/// Absolute y-coordinate of the touch position.
y: f64,
},
/// Movement of a touch on the device surface to a given position (x, y).
///
/// If the device has multi-touch capabilities a slot is given.
Motion {
/// `TouchSlot`, if the device has multi-touch capabilities
slot: Option<TouchSlot>,
/// Absolute x-coordinate of the final touch position after the motion.
x: f64,
/// Absolute y-coordinate of the final touch position after the motion.
y: f64,
},
/// Stop of an event chain.
///
/// If the device has multi-touch capabilities a slot is given.
Up {
/// `TouchSlot`, if the device has multi-touch capabilities
slot: Option<TouchSlot>,
},
/// Cancel of an event chain. All previous events in the chain should be ignored.
///
/// If the device has multi-touch capabilities a slot is given.
Cancel {
/// `TouchSlot`, if the device has multi-touch capabilities
slot: Option<TouchSlot>,
},
/// Signals the end of a set of touchpoints at one device sample time.
Frame,
}
/// Trait that describes objects providing a source of input events. All input backends
/// need to implemenent this and provide the same base gurantees about the presicion of
/// given events.
pub trait InputBackend: Sized {
/// Type of input device associated with the backend
type InputConfig;
/// Type representing errors that may be returned when processing events
type EventError: Error;
/// Sets a new handler for this `InputBackend`
fn set_handler<H: InputHandler<Self> + 'static>(&mut self, handler: H);
/// Get a reference to the currently set handler, if any
fn get_handler(&mut self) -> Option<&mut InputHandler<Self>>;
/// Clears the currently handler, if one is set
fn clear_handler(&mut self);
/// Get current `InputConfig`
fn input_config(&mut self) -> &mut Self::InputConfig;
/// Processes new events of the underlying backend and drives the `InputHandler`.
fn dispatch_new_events(&mut self) -> Result<(), Self::EventError>;
/// Sets the cursor position, useful for e.g. pointer wrapping.
///
/// Not guaranteed to be supported on every backend. The result usually
/// depends on the capability of the backend, but may also fail for certain
/// specific actions. See the backends documentation.
fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()>;
}
/// Implement to receive input events from any `InputBackend`.
pub trait InputHandler<B: InputBackend> {
/// Called when a new `Seat` has been created
fn on_seat_created(&mut self, seat: &Seat);
/// Called when an existing `Seat` has been destroyed.
fn on_seat_destroyed(&mut self, seat: &Seat);
/// Called when a `Seat`'s properties have changed.
fn on_seat_changed(&mut self, seat: &Seat);
/// Called when a new keyboard event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
/// - `key_code` - Code of the pressed key. See linux/input-event-codes.h
/// - `state` - `KeyState` of the event
/// - `count` - Total number of keys pressed on all devices on the associated `Seat`
///
/// # TODO:
/// - check if events can arrive out of order.
/// - Make stronger time guarantees
fn on_keyboard_key(&mut self, seat: &Seat, time: u32, key_code: u32, state: KeyState, count: u32);
/// Called when a new pointer movement event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
/// - `to` - Absolute screen coordinates of the pointer moved to.
///
/// # TODO:
/// - check if events can arrive out of order.
/// - Make stronger time guarantees
fn on_pointer_move(&mut self, seat: &Seat, time: u32, to: (u32, u32));
/// Called when a new pointer button event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
/// - `button` - Which button was pressed..
/// - `state` - `MouseButtonState` of the event
///
/// # TODO:
/// - check if events can arrive out of order.
/// - Make stronger time guarantees
fn on_pointer_button(&mut self, seat: &Seat, time: u32, button: MouseButton, state: MouseButtonState);
/// Called when a new pointer scroll event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
/// - `axis` - `Axis` this event was generated for.
/// - `source` - Source of the scroll event. Important for interpretation of `amount`.
/// - `amount` - Amount of scrolling on the given `Axis`. See `source` for interpretation.
///
/// # TODO:
/// - check if events can arrive out of order.
/// - Make stronger time guarantees
fn on_pointer_scroll(&mut self, seat: &Seat, time: u32, axis: Axis, source: AxisSource, amount: f64);
/// Called when a new touch event was received.
///
/// # Arguments
///
/// - `seat` - The `Seat` the event belongs to
/// - `time` - A upward counting variable useful for event ordering. Makes no gurantees about actual time passed between events.
/// - `event` - Touch event recieved. See `TouchEvent`.
///
/// # TODO:
/// - check if events can arrive out of order.
/// - Make stronger time guarantees
fn on_touch(&mut self, seat: &Seat, time: u32, event: TouchEvent);
/// Called when the `InputConfig` was changed through an external event.
///
/// What kind of events can trigger this call is completely backend dependent.
/// E.g. an input devices was attached/detached or changed it's own configuration.
fn on_input_config_changed(&mut self, config: &mut B::InputConfig);
}
impl<B: InputBackend> InputHandler<B> for Box<InputHandler<B>> {
fn on_seat_created(&mut self, seat: &Seat) {
(**self).on_seat_created(seat)
}
fn on_seat_destroyed(&mut self, seat: &Seat) {
(**self).on_seat_destroyed(seat)
}
fn on_seat_changed(&mut self, seat: &Seat) {
(**self).on_seat_changed(seat)
}
fn on_keyboard_key(&mut self, seat: &Seat, time: u32, key_code: u32, state: KeyState, count: u32) {
(**self).on_keyboard_key(seat, time, key_code, state, count)
}
fn on_pointer_move(&mut self, seat: &Seat, time: u32, to: (u32, u32)) {
(**self).on_pointer_move(seat, time, to)
}
fn on_pointer_button(&mut self, seat: &Seat, time: u32, button: MouseButton, state: MouseButtonState) {
(**self).on_pointer_button(seat, time, button, state)
}
fn on_pointer_scroll(&mut self, seat: &Seat, time: u32, axis: Axis, source: AxisSource, amount: f64) {
(**self).on_pointer_scroll(seat, time, axis, source, amount)
}
fn on_touch(&mut self, seat: &Seat, time: u32, event: TouchEvent) {
(**self).on_touch(seat, time, event)
}
fn on_input_config_changed(&mut self, config: &mut B::InputConfig) {
(**self).on_input_config_changed(config)
}
}

34
src/backend/mod.rs Normal file
View File

@ -0,0 +1,34 @@
//! Backend (rendering/input) creation helpers
//!
//! Collection of common traits and implementation about
//! rendering onto various targets and receiving input
//! from various sources.
//!
//! Supported graphics backends:
//!
//! - glutin (headless/windowed)
//!
//! Supported input backends:
//!
//! - glutin (windowed)
pub mod input;
pub mod graphics;
#[cfg(feature = "backend_glutin")]
pub mod glutin;
#[cfg(feature = "renderer_glium")]
mod glium;
#[cfg(feature = "renderer_glium")]
pub use glium::*;
/// Internal functions that need to be accessible by the different backend implementations
trait SeatInternal {
fn new(id: u32, capabilities: input::SeatCapabilities) -> Self;
}
trait TouchSlotInternal {
fn new(id: u32) -> Self;
}

View File

@ -7,8 +7,15 @@
extern crate wayland_server; extern crate wayland_server;
extern crate nix; extern crate nix;
#[cfg(feature = "backend_glutin")]
extern crate glutin;
#[cfg(feature = "renderer_glium")]
extern crate glium;
#[macro_use] #[macro_use]
extern crate slog; extern crate slog;
extern crate slog_stdlog; extern crate slog_stdlog;
pub mod shm; pub mod shm;
pub mod backend;