Merge pull request #9 from Drakulix/feature/glutin
First draft of a glutin backend
This commit is contained in:
commit
f40bd92e14
|
@ -4,8 +4,8 @@ fn_args_layout = "Visual"
|
|||
fn_arg_intent = "Tabbed"
|
||||
reorder_imports = true
|
||||
reorder_imported_names = true
|
||||
report_todo = "Always"
|
||||
report_fixme = "Always"
|
||||
report_todo = "Never"
|
||||
report_fixme = "Never"
|
||||
normalize_comments = true
|
||||
use_try_shorthand = true
|
||||
max_width = 110
|
||||
|
|
|
@ -26,6 +26,8 @@ branches:
|
|||
before_script:
|
||||
- export PATH=$HOME/.local/bin:$HOME/.cargo/bin:$PATH
|
||||
- which rustfmt || cargo install rustfmt
|
||||
- which cargo-install-update || cargo install cargo-update
|
||||
- cargo install-update -a
|
||||
- pip install 'travis-cargo<0.2' --user
|
||||
- mkdir $(pwd)/socket
|
||||
- export XDG_RUNTIME_DIR="$(pwd)/socket"
|
||||
|
|
|
@ -4,11 +4,18 @@ version = "0.1.0"
|
|||
authors = ["Victor Berger <victor.berger@thalesgroup.com>"]
|
||||
|
||||
[dependencies]
|
||||
wayland-server = "0.8.4"
|
||||
wayland-server = "0.8.6"
|
||||
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 = ["backend_glutin", "renderer_glium"]
|
||||
backend_glutin = ["glutin", "wayland-server/dlopen"]
|
||||
renderer_glium = ["glium"]
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -7,8 +7,15 @@
|
|||
extern crate wayland_server;
|
||||
extern crate nix;
|
||||
|
||||
#[cfg(feature = "backend_glutin")]
|
||||
extern crate glutin;
|
||||
|
||||
#[cfg(feature = "renderer_glium")]
|
||||
extern crate glium;
|
||||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
extern crate slog_stdlog;
|
||||
|
||||
pub mod shm;
|
||||
pub mod backend;
|
||||
|
|
Loading…
Reference in New Issue