wlroots 0.4.0

Wayland compositor framework
extern crate log;
#[macro_use]
extern crate wlroots;

use std::f64::consts::PI;

use log::LevelFilter;

use wlroots::{area::{Area, Size, Origin}, compositor,
              input::{self, keyboard, tablet_tool, tablet_pad},
              output, render::matrix,
              wlroots_sys::wl_output_transform::WL_OUTPUT_TRANSFORM_NORMAL,
              wlr_tablet_tool_proximity_state::*,
              wlr_button_state::*,
              utils::log::Logger,
              xkbcommon::xkb::KEY_Escape};

#[derive(Debug, Default)]
struct State {
    proximity: bool,
    tap: bool,
    button: bool,
    distance: f64,
    pressure: f64,
    pos: (f64, f64),
    tilt: (f64, f64),
    size_mm: (f64, f64),
    ring: f64,
    tool_color: [f32; 4],
    pad_color: [f32; 4]
}

impl State {
    fn new() -> Self {
        State { tool_color: [1.0, 1.0, 1.0, 1.0],
                pad_color: [0.5, 0.5, 0.5, 1.0],
                ..State::default() }
    }
}

struct OutputEx;
struct KeyboardEx;
struct TabletEx;

fn keyboard_added(_: compositor::Handle,
                  _: keyboard::Handle)
                  -> Option<Box<keyboard::Handler>> {
    Some(Box::new(KeyboardEx))
}

fn tablet_tool_added(compositor: compositor::Handle,
                     tool: tablet_tool::Handle)
                     -> Option<Box<tablet_tool::Handler>> {
    with_handles!([(compositor: {compositor}), (tool: {tool})] => {
        let state: &mut State = compositor.downcast();
        state.size_mm = tool.input_device().size();
        if state.size_mm.0 == 0.0 {
            state.size_mm.0 = 20.0;
        }
        if state.size_mm.1 == 0.0 {
            state.size_mm.1 = 10.0;
        }
    }).unwrap();
    Some(Box::new(TabletEx))
}

fn tablet_pad_added(_: compositor::Handle,
                    _: tablet_pad::Handle)
                    -> Option<Box<tablet_pad::Handler>> {
    Some(Box::new(TabletEx))
}

fn output_added<'output>(_: compositor::Handle,
                         builder: output::Builder<'output>)
                         -> Option<output::BuilderResult<'output>> {
    let result = builder.build_best_mode(OutputEx);
    Some(result)
}

impl keyboard::Handler for KeyboardEx {
    fn on_key(&mut self,
              _: compositor::Handle,
              _: keyboard::Handle,
              key_event: &keyboard::event::Key) {
        for key in key_event.pressed_keys() {
            if key == KEY_Escape {
                compositor::terminate()
            }
        }
    }
}

impl tablet_pad::Handler for TabletEx {
    fn on_button(&mut self,
                 compositor: compositor::Handle,
                 _: tablet_pad::Handle,
                 event: &tablet_pad::event::Button) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.downcast();
            if event.state() == WLR_BUTTON_RELEASED {
                state.pad_color = [0.5, 0.5, 0.5, 1.0];
            } else {
                for i in 0..3 {
                    state.pad_color[i] = if event.button() % 3 == i as u32 {
                        0.0
                    } else {
                        1.0
                    }
                }
            }
        }).unwrap();
    }

    fn on_ring(&mut self,
               compositor: compositor::Handle,
               _: tablet_pad::Handle,
               event: &tablet_pad::event::Ring) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.downcast();
            let position = event.position();
            if position != -1.0 {
                state.ring = -(position * PI / 180.0)
            }
        }).unwrap();
    }
}

impl tablet_tool::Handler for TabletEx {
    fn on_axis(&mut self,
               compositor: compositor::Handle,
               _: tablet_tool::Handle,
               event: &tablet_tool::event::Axis) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.downcast();
            let axis = event.updated_axes();
            let (x, y) = event.position();
            let (tilt_x, tilt_y) = event.tilt();
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_X) {
                state.pos.0 = x
            }
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_Y) {
                state.pos.1 = y
            }
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_DISTANCE) {
                state.distance = event.distance()
            }
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_PRESSURE) {
                state.pressure = event.pressure()
            }
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_TILT_X) {
                state.tilt.0 = tilt_x
            }
            if axis.contains(tablet_tool::Axis::WLR_TABLET_TOOL_AXIS_TILT_Y) {
                state.tilt.1 = tilt_y
            }
        }).unwrap();
    }

    fn on_proximity(&mut self,
                    compositor: compositor::Handle,
                    _: tablet_tool::Handle,
                    event: &tablet_tool::event::Proximity) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.downcast();
            state.proximity = event.state() == WLR_TABLET_TOOL_PROXIMITY_IN
        }).unwrap();
    }

    fn on_button(&mut self,
                 compositor: compositor::Handle,
                 _: tablet_tool::Handle,
                 event: &tablet_tool::event::Button) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.downcast();
            if event.state() == WLR_BUTTON_RELEASED {
                state.button = false;
            } else {
                state.button = true;
                for i in 0..3 {
                    state.tool_color[i] = if event.button() % 3 == i as u32 {
                        0.0
                    } else {
                        1.0
                    };
                }
            }
        }).unwrap();
    }
}

impl output::Handler for OutputEx {
    fn on_frame(&mut self, compositor: compositor::Handle, output: output::Handle) {
        with_handles!([(compositor: {compositor}), (output: {output})] => {
            let state: &mut State = compositor.data.downcast_mut().unwrap();
            let (width, height) = output.effective_resolution();
            let renderer = compositor.renderer
                .as_mut()
                .expect("Compositor was not loaded with a renderer");
            let mut renderer = renderer.render(output, None);
            renderer.clear([0.25, 0.25, 0.25, 1.0]);
            let tool_color: [f32; 4] = if state.button {
                state.tool_color.clone()
            } else {
                let distance: f64 = 0.8 * (1.0 - state.distance);
                [distance as f32, distance as f32, distance as f32, 1.0]
            };
            let scale = 4.0;
            let pad_width = (state.size_mm.0 * scale) as f32;
            let pad_height = (state.size_mm.1 * scale) as f32;
            let left: f32 = (width as f64 / 2.0 - pad_width as f64 / 2.0) as f32;
            let top: f32 = (height as f64 / 2.0 - pad_height as f64 / 2.0) as f32;
            let area = Area::new(Origin::new(left as i32, top as i32),
                                 Size::new(pad_width as i32, pad_height as i32));
            let transform_matrix = renderer.output.transform_matrix();
            renderer.render_colored_rect(area, state.pad_color, transform_matrix.clone());
            if state.proximity {
                let origin =
                    Origin { x: ((state.pos.0 * pad_width as f64) - 8.0 * (state.pressure + 1.0)
                                 + left as f64) as i32,
                             y: ((state.pos.1 * pad_height as f64) - 8.0 * (state.pressure + 1.0)
                                 + top as f64) as i32 };
                let size = Size { width: (16.0 * (state.pressure + 1.0)) as i32,

                                  height: (16.0 * (state.pressure + 1.0)) as i32 };
                let mut area = Area { origin, size };
                let matrix = matrix::project_box(area,
                                                 WL_OUTPUT_TRANSFORM_NORMAL,
                                                 state.ring as _,
                                                 transform_matrix.clone());
                renderer.render_colored_quad(tool_color, matrix);

                area.origin.x += state.tilt.0 as i32;
                area.origin.y += state.tilt.1 as i32;
                area.size.width /= 2;
                area.size.width /= 2;
                renderer.render_colored_rect(area, tool_color, transform_matrix);
            }
        }).unwrap();
    }
}

fn main() {
    Logger::init(LevelFilter::Debug, None);
    let output_builder = output::manager::Builder::default().output_added(output_added);
    let input_builder = input::manager::Builder::default()
        .keyboard_added(keyboard_added)
        .tablet_tool_added(tablet_tool_added)
        .tablet_pad_added(tablet_pad_added);
    compositor::Builder::new().gles2(true)
                            .input_manager(input_builder)
                            .output_manager(output_builder)
                            .build_auto(State::new())
                            .run()
}