Initial glutin backend implementation
This commit is contained in:
parent
30721f24df
commit
ec8149b084
|
@ -4,11 +4,16 @@ version = "0.1.0"
|
|||
authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
|
||||
|
||||
[dependencies]
|
||||
wayland-server = "0.8.4"
|
||||
wayland-server = { version = "0.8.4", features = ["dlopen"] }
|
||||
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-stdlog = "~1.1.0"
|
||||
clippy = { version = "*", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
slog-term = "~1.5"
|
||||
|
||||
[features]
|
||||
default = ["glutin"]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
extern crate wayland_server;
|
||||
extern crate smithay;
|
||||
|
||||
use smithay::shm::ShmGlobal;
|
||||
use smithay::backend::glutin;
|
||||
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.process_new_events();
|
||||
|
||||
event_loop.run();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
use glium::backend::Backend;
|
||||
use glium::SwapBuffersError as GliumSwapBuffersError;
|
||||
|
||||
use ::backend::graphics::opengl::{OpenglRenderer, SwapBuffersError};
|
||||
|
||||
impl From<SwapBuffersError> for GliumSwapBuffersError
|
||||
{
|
||||
fn from(error: SwapBuffersError) -> Self {
|
||||
match error {
|
||||
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
|
||||
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: OpenglRenderer> Backend for T
|
||||
{
|
||||
fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
|
||||
self.swap_buffers().map_err(|x| x.into)
|
||||
}
|
||||
|
||||
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void
|
||||
{
|
||||
self.get_proc_address(symbol)
|
||||
}
|
||||
|
||||
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
|
||||
self.get_framebuffer_dimensions()
|
||||
}
|
||||
|
||||
fn is_current(&self) -> bool {
|
||||
self.is_current()
|
||||
}
|
||||
|
||||
unsafe fn make_current(&self) {
|
||||
self.make_current()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
//! Implementation of backend traits for types provided by `glutin`
|
||||
|
||||
use glutin::{ContextError, CreationError, Event, ElementState, MouseScrollDelta, Touch, TouchPhase, GlContext, HeadlessRendererBuilder, HeadlessContext, WindowBuilder, Window};
|
||||
use glutin::{Api as GlutinApi, PixelFormat as GlutinPixelFormat, MouseButton as GlutinMouseButton};
|
||||
use nix::c_void;
|
||||
use std::rc::Rc;
|
||||
|
||||
use backend::NewIdType;
|
||||
use backend::graphics::opengl::{Api, OpenglRenderer, PixelFormat, SwapBuffersError};
|
||||
use backend::input::{InputBackend, InputHandler, Seat, KeyState, MouseButton, MouseButtonState, Axis, AxisSource, TouchEvent, TouchSlot};
|
||||
|
||||
/// 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 `OpenglRenderer` 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 OpenglRenderer 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
|
||||
/// `OpenglRenderer` graphics backend trait.
|
||||
pub struct GlutinWindowedRenderer
|
||||
{
|
||||
window: Rc<Window>
|
||||
}
|
||||
|
||||
impl GlutinWindowedRenderer
|
||||
{
|
||||
fn new(window: Rc<Window>) -> GlutinWindowedRenderer {
|
||||
GlutinWindowedRenderer {
|
||||
window: window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenglRenderer 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`
|
||||
pub enum GlutinInputError
|
||||
{
|
||||
/// The underlying `glutin` `Window` was closed. No further events can be processed.
|
||||
///
|
||||
/// See `GlutinInputBackend::process_new_events`.
|
||||
WindowClosed
|
||||
}
|
||||
|
||||
/// 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,
|
||||
handler: Option<Box<InputHandler + 'static>>,
|
||||
}
|
||||
|
||||
impl InputBackend for GlutinInputBackend
|
||||
{
|
||||
fn set_handler<H: InputHandler + '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.handler.as_mut().map(|handler| handler as &mut InputHandler)
|
||||
}
|
||||
|
||||
fn clear_handler(&mut self) {
|
||||
if let Some(ref mut handler) = self.handler {
|
||||
handler.on_seat_destroyed(&self.seat);
|
||||
}
|
||||
self.handler = None;
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlutinInputBackend
|
||||
{
|
||||
fn new(window: Rc<Window>) -> GlutinInputBackend
|
||||
{
|
||||
GlutinInputBackend {
|
||||
window: window,
|
||||
time_counter: 0,
|
||||
seat: Seat::new(0),
|
||||
handler: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn process_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/*TODO: Is this really the keycode? glutins docs don't tell*/, _) => 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 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -0,0 +1,87 @@
|
|||
//! 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
|
||||
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,
|
||||
/// bits used for colors
|
||||
pub color_bits: u8,
|
||||
/// bits used for alpha channel
|
||||
pub alpha_bits: u8,
|
||||
/// bits used for depth channel
|
||||
pub depth_bits: u8,
|
||||
/// bits used for stencil buffer
|
||||
pub stencil_bits: u8,
|
||||
/// is stereoscopy enabled
|
||||
pub stereoscopy: bool,
|
||||
/// is double buffering enabled
|
||||
pub double_buffer: bool,
|
||||
/// multisampling format used if enabled
|
||||
pub multisampling: Option<u16>,
|
||||
/// if the format has srgb enabled
|
||||
pub srgb: bool,
|
||||
}
|
||||
|
||||
/// Trait that describes objects that have an OpenGl context
|
||||
/// and can be used to render upon
|
||||
pub trait OpenglRenderer
|
||||
{
|
||||
/// 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.
|
||||
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.
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
//! Common traits and types used for software rendering on graphics backends
|
||||
|
||||
use wayland_server::protocol::wl_shm::Format;
|
||||
use std::error::Error;
|
||||
|
||||
/// Trait that describes objects providing a software rendering implementation
|
||||
pub trait CpuRender<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);
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
//! Common traits for input backends to receive input from.
|
||||
|
||||
use backend::NewIdType;
|
||||
|
||||
/// 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 }
|
||||
|
||||
impl NewIdType for Seat
|
||||
{
|
||||
fn new(id: u32) -> Seat
|
||||
{
|
||||
Seat { id: id }
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 NewIdType for TouchSlot
|
||||
{
|
||||
fn new(id: u32) -> TouchSlot
|
||||
{
|
||||
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 {
|
||||
/// Sets a new handler for this `InputBackend`
|
||||
fn set_handler<H: InputHandler + 'static>(&mut self, handler: H);
|
||||
/// Get a reference to the currently set handler, if any
|
||||
fn get_handler(&mut self) -> Option<&mut InputHandler>;
|
||||
/// Clears the currently handler, if one is set
|
||||
fn clear_handler(&mut self);
|
||||
/// 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 {
|
||||
/// 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 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` - Amount of key presses registered
|
||||
///
|
||||
/// # 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 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.
|
||||
/// - `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 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.
|
||||
/// - `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 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.
|
||||
/// - `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 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.
|
||||
/// - `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);
|
||||
}
|
||||
|
||||
impl InputHandler for Box<InputHandler> {
|
||||
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_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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//! 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 = "glutin")]
|
||||
pub mod glutin;
|
||||
|
||||
#[cfg(feature = "glium")]
|
||||
mod glium;
|
||||
#[cfg(feature = "glium")]
|
||||
pub use glium::*;
|
||||
|
||||
trait NewIdType {
|
||||
fn new(id: u32) -> Self;
|
||||
}
|
|
@ -7,8 +7,12 @@
|
|||
extern crate wayland_server;
|
||||
extern crate nix;
|
||||
|
||||
#[cfg(feature = "glutin")]
|
||||
extern crate glutin;
|
||||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_stdlog;
|
||||
|
||||
pub mod shm;
|
||||
pub mod backend;
|
||||
|
|
Loading…
Reference in New Issue