606 lines
18 KiB
Rust
606 lines
18 KiB
Rust
mod buffer;
|
|||
|
|
mod color;
|
||
|
|
mod draw;
|
||
|
|
mod fonts;
|
||
|
|
mod keyboard;
|
||
|
|
mod utils;
|
||
|
|
mod widgets;
|
||
|
|
|
||
|
|
use wayland_client::{
|
||
|
|
protocol::{
|
||
|
|
wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool,
|
||
|
|
wl_surface,
|
||
|
|
},
|
||
|
|
Connection, Dispatch, QueueHandle, WEnum, WaylandSource,
|
||
|
|
};
|
||
|
|
|
||
|
|
use wayland_protocols::xdg::{
|
||
|
|
activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1},
|
||
|
|
shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base},
|
||
|
|
};
|
||
|
|
|
||
|
|
use calloop::{
|
||
|
|
timer::{TimeoutAction, Timer},
|
||
|
|
EventLoop,
|
||
|
|
};
|
||
|
|
use chrono::{Duration, Local, Timelike};
|
||
|
|
|
||
|
|
use buffer::{BufferManager, BufferView};
|
||
|
|
use color::Color;
|
||
|
|
use fonts::FontMap;
|
||
|
|
use keyboard::Keyboard;
|
||
|
|
use widgets::{
|
||
|
|
Battery, Clock, Date, Geometry, HorizontalLayout, IndexedLayout, Interface, Layout, Line,
|
||
|
|
Margin, VerticalLayout, Widget, WidgetUpdater,
|
||
|
|
};
|
||
|
|
|
||
|
|
use std::{cell::RefCell, env, mem, rc::Rc, thread};
|
||
|
|
|
||
|
|
enum MaybeFontMap {
|
||
|
|
Waiting(thread::JoinHandle<FontMap>),
|
||
|
|
Ready(Rc<RefCell<FontMap>>),
|
||
|
|
Invalid,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl MaybeFontMap {
|
||
|
|
fn unwrap(&self) -> Rc<RefCell<FontMap>> {
|
||
|
|
match self {
|
||
|
|
MaybeFontMap::Ready(f) => f.clone(),
|
||
|
|
_ => panic!("fontmap not yet ready"),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn resolve(&mut self) {
|
||
|
|
if matches!(self, MaybeFontMap::Waiting(_)) {
|
||
|
|
let s = mem::replace(self, MaybeFontMap::Invalid);
|
||
|
|
match s {
|
||
|
|
MaybeFontMap::Waiting(handle) => {
|
||
|
|
*self = MaybeFontMap::Ready(Rc::new(RefCell::new(handle.join().unwrap())));
|
||
|
|
}
|
||
|
|
_ => unreachable!(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct State {
|
||
|
|
running: bool,
|
||
|
|
dirty: bool,
|
||
|
|
base_surface: Option<wl_surface::WlSurface>,
|
||
|
|
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
||
|
|
xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>,
|
||
|
|
wl_shm: Option<wl_shm::WlShm>,
|
||
|
|
xdg_activation: Option<xdg_activation_v1::XdgActivationV1>,
|
||
|
|
configured: bool,
|
||
|
|
dimensions: (i32, i32),
|
||
|
|
bufmgr: BufferManager,
|
||
|
|
widgets: Vec<Box<dyn Widget>>,
|
||
|
|
keyboard: Keyboard,
|
||
|
|
fonts: MaybeFontMap,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl State {
|
||
|
|
fn add_buffer(&mut self, qh: &QueueHandle<Self>) {
|
||
|
|
self.bufmgr.add_buffer(
|
||
|
|
self.wl_shm.as_ref().expect("missing wl_shm"),
|
||
|
|
self.dimensions,
|
||
|
|
qh,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
fn activate(&mut self, qh: &QueueHandle<Self>) {
|
||
|
|
let key = match env::var("XDG_ACTIVATION_TOKEN") {
|
||
|
|
Ok(token) => token,
|
||
|
|
Err(_) => return,
|
||
|
|
};
|
||
|
|
match (&self.base_surface, &self.xdg_activation) {
|
||
|
|
(Some(surface), Some(activation)) => {
|
||
|
|
activation.activate(key, surface);
|
||
|
|
let activation_token = activation.get_activation_token(qh, ());
|
||
|
|
activation_token.commit();
|
||
|
|
env::remove_var("XDG_ACTIVATION_TOKEN");
|
||
|
|
}
|
||
|
|
_ => (),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl WidgetUpdater for State {
|
||
|
|
fn geometry_update(
|
||
|
|
&mut self,
|
||
|
|
idx: usize,
|
||
|
|
fonts: &mut FontMap,
|
||
|
|
geometry: &Geometry,
|
||
|
|
) -> Geometry {
|
||
|
|
self.widgets[idx].geometry_update(fonts, geometry)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
let font_thread = thread::spawn(move || {
|
||
|
|
let mut fm = FontMap::new();
|
||
|
|
// Hard-coding font paths make things a lot faster
|
||
|
|
fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 96.);
|
||
|
|
fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 40.);
|
||
|
|
fm.queue_font_path("sans", "/usr/share/fonts/noto/NotoSans-Regular.ttf", 24.);
|
||
|
|
fm.queue_font_path(
|
||
|
|
"monospace",
|
||
|
|
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf",
|
||
|
|
32.,
|
||
|
|
);
|
||
|
|
fm.load_fonts();
|
||
|
|
fm
|
||
|
|
});
|
||
|
|
let conn = Connection::connect_to_env().unwrap();
|
||
|
|
|
||
|
|
let event_queue = conn.new_event_queue();
|
||
|
|
let qhandle: QueueHandle<State> = event_queue.handle();
|
||
|
|
|
||
|
|
let display = conn.display();
|
||
|
|
display.get_registry(&qhandle, ());
|
||
|
|
|
||
|
|
let bufmgr = BufferManager {
|
||
|
|
buffers: Vec::new(),
|
||
|
|
};
|
||
|
|
|
||
|
|
let (ping_sender, ping_source) = calloop::ping::make_ping().unwrap();
|
||
|
|
|
||
|
|
let clock = Box::new(Clock::new());
|
||
|
|
let date = Box::new(Date::new());
|
||
|
|
let line = Box::new(Line::new());
|
||
|
|
let launcher = Box::new(Interface::new());
|
||
|
|
let battery = Box::new(Battery::new(ping_sender));
|
||
|
|
|
||
|
|
let mut event_loop: EventLoop<State> =
|
||
|
|
EventLoop::try_new().expect("Failed to initialize the event loop!");
|
||
|
|
|
||
|
|
let handle = event_loop.handle();
|
||
|
|
handle
|
||
|
|
.insert_source(
|
||
|
|
WaylandSource::new(event_queue).expect("Could not create WaylandSource!"),
|
||
|
|
|_event, queue, mut state| queue.dispatch_pending(&mut state),
|
||
|
|
)
|
||
|
|
.expect("Failed to insert event source!");
|
||
|
|
|
||
|
|
handle
|
||
|
|
.insert_source(ping_source, |(), &mut (), state| {
|
||
|
|
state.widgets[2].set_dirty(true);
|
||
|
|
state.dirty = true;
|
||
|
|
})
|
||
|
|
.expect("Failed to insert ping source!");
|
||
|
|
|
||
|
|
let clock_source = Timer::from_duration(std::time::Duration::from_secs(1));
|
||
|
|
handle
|
||
|
|
.insert_source(clock_source, |_event, _metadata, state| {
|
||
|
|
let now = Local::now().naive_local();
|
||
|
|
let target = (now + Duration::seconds(60))
|
||
|
|
.with_second(0)
|
||
|
|
.unwrap()
|
||
|
|
.with_nanosecond(0)
|
||
|
|
.unwrap();
|
||
|
|
let d = target - now;
|
||
|
|
state.widgets[0].set_dirty(true);
|
||
|
|
state.widgets[1].set_dirty(true);
|
||
|
|
state.dirty = true;
|
||
|
|
|
||
|
|
// The timer event source requires us to return a TimeoutAction to
|
||
|
|
// specify if the timer should be rescheduled. In our case we just drop it.
|
||
|
|
TimeoutAction::ToDuration(d.to_std().unwrap())
|
||
|
|
})
|
||
|
|
.expect("Failed to insert event source!");
|
||
|
|
|
||
|
|
let mut state = State {
|
||
|
|
running: true,
|
||
|
|
dirty: true,
|
||
|
|
base_surface: None,
|
||
|
|
wm_base: None,
|
||
|
|
xdg_surface: None,
|
||
|
|
wl_shm: None,
|
||
|
|
xdg_activation: None,
|
||
|
|
configured: false,
|
||
|
|
dimensions: (320, 240),
|
||
|
|
bufmgr: bufmgr,
|
||
|
|
widgets: vec![clock, date, battery, line, launcher],
|
||
|
|
keyboard: Keyboard::new(),
|
||
|
|
fonts: MaybeFontMap::Waiting(font_thread),
|
||
|
|
};
|
||
|
|
|
||
|
|
let mut damage = Vec::new();
|
||
|
|
for _ in 0..state.widgets.len() {
|
||
|
|
damage.push(Geometry::new());
|
||
|
|
}
|
||
|
|
|
||
|
|
while state.running {
|
||
|
|
event_loop
|
||
|
|
.dispatch(None, &mut state)
|
||
|
|
.expect("Could not dispatch event loop");
|
||
|
|
|
||
|
|
if !state.configured || !state.dirty {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut drew = false;
|
||
|
|
let mut force = false;
|
||
|
|
if state.bufmgr.buffers.len() == 0 {
|
||
|
|
state.add_buffer(&qhandle);
|
||
|
|
force = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// We expect to see the usual shm optimization
|
||
|
|
let buf = match state.bufmgr.next_buffer() {
|
||
|
|
Some(b) => b,
|
||
|
|
None => {
|
||
|
|
// We are still waiting for our buffer
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
state.dirty = false;
|
||
|
|
buf.acquire();
|
||
|
|
let mut bufview = BufferView::new(
|
||
|
|
&mut buf.mmap,
|
||
|
|
(state.dimensions.0 as u32, state.dimensions.1 as u32),
|
||
|
|
);
|
||
|
|
|
||
|
|
let bg = Color::new(0., 0., 0., 1.);
|
||
|
|
|
||
|
|
let surface = state.base_surface.as_ref().unwrap().clone();
|
||
|
|
for (idx, widget) in state.widgets.iter_mut().enumerate() {
|
||
|
|
if force || widget.get_dirty() {
|
||
|
|
widget.set_dirty(false);
|
||
|
|
drew = true;
|
||
|
|
|
||
|
|
let geo = widget.geometry();
|
||
|
|
let old_damage = damage[idx];
|
||
|
|
|
||
|
|
if !force {
|
||
|
|
bufview.subgeometry(old_damage).unwrap().memset(&bg);
|
||
|
|
} else {
|
||
|
|
bufview.subgeometry(geo).unwrap().memset(&bg);
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut subview = bufview.subgeometry(geo).unwrap();
|
||
|
|
let new_damage = widget.draw(&mut state.fonts.unwrap().borrow_mut(), &mut subview);
|
||
|
|
let combined_damage = new_damage.expand(old_damage);
|
||
|
|
damage[idx] = new_damage;
|
||
|
|
surface.damage_buffer(
|
||
|
|
combined_damage.x as i32,
|
||
|
|
combined_damage.y as i32,
|
||
|
|
combined_damage.width as i32,
|
||
|
|
combined_damage.height as i32,
|
||
|
|
);
|
||
|
|
//draw_box(&mut subview, &Color::new(1.0, 0.5, 0.0, 1.0), (geo.width, geo.height));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !drew {
|
||
|
|
buf.release();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if force {
|
||
|
|
surface.damage_buffer(0, 0, 0x7FFFFFFF, 0x7FFFFFFF)
|
||
|
|
}
|
||
|
|
surface.attach(Some(&buf.buffer), 0, 0);
|
||
|
|
surface.commit();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
registry: &wl_registry::WlRegistry,
|
||
|
|
event: wl_registry::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
qh: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
if let wl_registry::Event::Global {
|
||
|
|
name, interface, ..
|
||
|
|
} = event
|
||
|
|
{
|
||
|
|
match &interface[..] {
|
||
|
|
"wl_compositor" => {
|
||
|
|
let compositor =
|
||
|
|
registry.bind::<wl_compositor::WlCompositor, _, _>(name, 4, qh, ());
|
||
|
|
let surface = compositor.create_surface(qh, ());
|
||
|
|
state.base_surface = Some(surface);
|
||
|
|
state.activate(qh);
|
||
|
|
|
||
|
|
if state.wm_base.is_some() && state.xdg_surface.is_none() {
|
||
|
|
state.init_xdg_surface(qh);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"wl_shm" => {
|
||
|
|
state.wl_shm = Some(registry.bind::<wl_shm::WlShm, _, _>(name, 1, qh, ()));
|
||
|
|
}
|
||
|
|
"wl_seat" => {
|
||
|
|
registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ());
|
||
|
|
}
|
||
|
|
"xdg_wm_base" => {
|
||
|
|
let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
|
||
|
|
state.wm_base = Some(wm_base);
|
||
|
|
|
||
|
|
if state.base_surface.is_some() && state.xdg_surface.is_none() {
|
||
|
|
state.init_xdg_surface(qh);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"xdg_activation_v1" => {
|
||
|
|
state.xdg_activation = Some(
|
||
|
|
registry.bind::<xdg_activation_v1::XdgActivationV1, _, _>(name, 1, qh, ()),
|
||
|
|
);
|
||
|
|
state.activate(qh);
|
||
|
|
}
|
||
|
|
_ => {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_compositor::WlCompositor, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
_: &wl_compositor::WlCompositor,
|
||
|
|
_: wl_compositor::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_surface::WlSurface, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
_: &wl_surface::WlSurface,
|
||
|
|
_: wl_surface::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_shm::WlShm, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
_: &wl_shm::WlShm,
|
||
|
|
_: wl_shm::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_shm_pool::WlShmPool, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
_: &wl_shm_pool::WlShmPool,
|
||
|
|
_: wl_shm_pool::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_buffer::WlBuffer, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
buffer: &wl_buffer::WlBuffer,
|
||
|
|
_: wl_buffer::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
for buf in state.bufmgr.buffers.iter_mut() {
|
||
|
|
if &buf.buffer == buffer {
|
||
|
|
buf.release();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl State {
|
||
|
|
fn init_xdg_surface(&mut self, qh: &QueueHandle<State>) {
|
||
|
|
let wm_base = self.wm_base.as_ref().unwrap();
|
||
|
|
let base_surface = self.base_surface.as_ref().unwrap();
|
||
|
|
|
||
|
|
let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ());
|
||
|
|
let toplevel = xdg_surface.get_toplevel(qh, ());
|
||
|
|
toplevel.set_title("wldash".into());
|
||
|
|
|
||
|
|
base_surface.commit();
|
||
|
|
|
||
|
|
self.xdg_surface = Some((xdg_surface, toplevel));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
wm_base: &xdg_wm_base::XdgWmBase,
|
||
|
|
event: xdg_wm_base::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
if let xdg_wm_base::Event::Ping { serial } = event {
|
||
|
|
wm_base.pong(serial);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<xdg_surface::XdgSurface, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
xdg_surface: &xdg_surface::XdgSurface,
|
||
|
|
event: xdg_surface::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
if let xdg_surface::Event::Configure { serial, .. } = event {
|
||
|
|
xdg_surface.ack_configure(serial);
|
||
|
|
state.configured = true;
|
||
|
|
state.dirty = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
_: &xdg_toplevel::XdgToplevel,
|
||
|
|
event: xdg_toplevel::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
match event {
|
||
|
|
xdg_toplevel::Event::Configure { width, height, .. } => {
|
||
|
|
if (width, height) == (0, 0) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if state.dimensions != (width, height) {
|
||
|
|
state.bufmgr.clear_buffers();
|
||
|
|
state.dimensions = (width, height);
|
||
|
|
let mut layout = VerticalLayout {
|
||
|
|
widgets: vec![
|
||
|
|
Box::new(HorizontalLayout {
|
||
|
|
widgets: vec![
|
||
|
|
Box::new(IndexedLayout { widget_idx: 0 }),
|
||
|
|
Box::new(Margin {
|
||
|
|
widget: Box::new(IndexedLayout { widget_idx: 1 }),
|
||
|
|
margin: (16, 8, 0, 0),
|
||
|
|
}),
|
||
|
|
Box::new(Margin {
|
||
|
|
widget: Box::new(IndexedLayout { widget_idx: 2 }),
|
||
|
|
margin: (16, 8, 8, 0),
|
||
|
|
}),
|
||
|
|
],
|
||
|
|
}),
|
||
|
|
Box::new(IndexedLayout { widget_idx: 3 }),
|
||
|
|
Box::new(IndexedLayout { widget_idx: 4 }),
|
||
|
|
],
|
||
|
|
};
|
||
|
|
|
||
|
|
state.fonts.resolve();
|
||
|
|
let fonts = state.fonts.unwrap();
|
||
|
|
layout.geometry_update(
|
||
|
|
&mut fonts.borrow_mut(),
|
||
|
|
&Geometry {
|
||
|
|
x: 0,
|
||
|
|
y: 0,
|
||
|
|
width: width as u32,
|
||
|
|
height: height as u32,
|
||
|
|
},
|
||
|
|
state,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
xdg_toplevel::Event::Close => {
|
||
|
|
state.running = false;
|
||
|
|
}
|
||
|
|
_ => (),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_seat::WlSeat, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
seat: &wl_seat::WlSeat,
|
||
|
|
event: wl_seat::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
qh: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
if let wl_seat::Event::Capabilities {
|
||
|
|
capabilities: WEnum::Value(capabilities),
|
||
|
|
} = event
|
||
|
|
{
|
||
|
|
if capabilities.contains(wl_seat::Capability::Keyboard) {
|
||
|
|
seat.get_keyboard(qh, ());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
_: &wl_keyboard::WlKeyboard,
|
||
|
|
event: wl_keyboard::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
match event {
|
||
|
|
wl_keyboard::Event::Key {
|
||
|
|
serial,
|
||
|
|
time,
|
||
|
|
key,
|
||
|
|
state: kbstate,
|
||
|
|
} => {
|
||
|
|
if key == 1 {
|
||
|
|
state.running = false;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
let k = state.keyboard.key(serial, time, key, kbstate);
|
||
|
|
for widget in state.widgets.iter_mut() {
|
||
|
|
widget.keyboard_input(&k);
|
||
|
|
}
|
||
|
|
state.dirty = true;
|
||
|
|
}
|
||
|
|
wl_keyboard::Event::Modifiers {
|
||
|
|
mods_depressed,
|
||
|
|
mods_latched,
|
||
|
|
mods_locked,
|
||
|
|
group,
|
||
|
|
..
|
||
|
|
} => {
|
||
|
|
state
|
||
|
|
.keyboard
|
||
|
|
.modifiers(mods_depressed, mods_latched, mods_locked, group);
|
||
|
|
}
|
||
|
|
wl_keyboard::Event::Keymap { format, fd, size } => {
|
||
|
|
state.keyboard.keymap(format, fd, size);
|
||
|
|
}
|
||
|
|
_ => (),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<xdg_activation_v1::XdgActivationV1, ()> for State {
|
||
|
|
fn event(
|
||
|
|
_: &mut Self,
|
||
|
|
_: &xdg_activation_v1::XdgActivationV1,
|
||
|
|
_: xdg_activation_v1::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, ()> for State {
|
||
|
|
fn event(
|
||
|
|
state: &mut Self,
|
||
|
|
_: &xdg_activation_token_v1::XdgActivationTokenV1,
|
||
|
|
event: xdg_activation_token_v1::Event,
|
||
|
|
_: &(),
|
||
|
|
_: &Connection,
|
||
|
|
_: &QueueHandle<Self>,
|
||
|
|
) {
|
||
|
|
match event {
|
||
|
|
xdg_activation_token_v1::Event::Done { token } => {
|
||
|
|
for widget in state.widgets.iter_mut() {
|
||
|
|
widget.token_update(&token);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_ => (),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|