state: Add basic input handling
This commit is contained in:
parent
e7ecfc8903
commit
d0304415eb
6 changed files with 503 additions and 43 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -592,7 +592,7 @@ checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/Smithay/smithay.git?rev=7e2affd#7e2affdf3106b22cb17ef48c087d3f0229a0abdf"
|
source = "git+https://github.com/Smithay/smithay.git?rev=b683f7d#b683f7d4ddcf3dacd593ce5d46d0a6fffcbe2ac0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appendlist",
|
"appendlist",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,6 @@ slog-stdlog = "4.1"
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
git = "https://github.com/Smithay/smithay.git"
|
git = "https://github.com/Smithay/smithay.git"
|
||||||
rev = "7e2affd"
|
rev = "b683f7d"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"]
|
features = ["backend_x11", "backend_egl", "desktop", "use_system_lib", "renderer_gl", "wayland_frontend"]
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
input::{set_active_output, Devices},
|
||||||
state::{BackendData, State},
|
state::{BackendData, State},
|
||||||
utils::GlobalDrop,
|
utils::GlobalDrop,
|
||||||
};
|
};
|
||||||
|
|
@ -10,10 +11,11 @@ use smithay::{
|
||||||
allocator::dmabuf::Dmabuf,
|
allocator::dmabuf::Dmabuf,
|
||||||
drm::DrmNode,
|
drm::DrmNode,
|
||||||
egl::{EGLContext, EGLDisplay},
|
egl::{EGLContext, EGLDisplay},
|
||||||
|
input::{Event, InputEvent},
|
||||||
renderer::{gles2::Gles2Renderer, Bind, ImportDma, ImportEgl, Unbind},
|
renderer::{gles2::Gles2Renderer, Bind, ImportDma, ImportEgl, Unbind},
|
||||||
x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Surface},
|
x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface},
|
||||||
},
|
},
|
||||||
desktop::Space,
|
desktop::{layer_map_for_output, Space},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{ping, EventLoop, LoopHandle},
|
calloop::{ping, EventLoop, LoopHandle},
|
||||||
gbm::Device as GbmDevice,
|
gbm::Device as GbmDevice,
|
||||||
|
|
@ -146,6 +148,7 @@ impl Surface {
|
||||||
&self.output,
|
&self.output,
|
||||||
age as usize,
|
age as usize,
|
||||||
[0.153, 0.161, 0.165, 1.0],
|
[0.153, 0.161, 0.165, 1.0],
|
||||||
|
&[],
|
||||||
) {
|
) {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
slog_scope::trace!("Finished rendering");
|
slog_scope::trace!("Finished rendering");
|
||||||
|
|
@ -156,6 +159,7 @@ impl Surface {
|
||||||
}
|
}
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
let _ = renderer.unbind();
|
let _ = renderer.unbind();
|
||||||
|
self.render.ping();
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.surface.reset_buffers();
|
self.surface.reset_buffers();
|
||||||
|
|
@ -207,56 +211,64 @@ pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Res
|
||||||
|
|
||||||
event_loop
|
event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(backend, |event, window, state| match event {
|
.insert_source(backend, |event, _, state| match event {
|
||||||
X11Event::CloseRequested => {
|
X11Event::CloseRequested { window_id } => {
|
||||||
window.unmap();
|
|
||||||
// TODO: drain_filter
|
// TODO: drain_filter
|
||||||
for output in state
|
for surface in state
|
||||||
.backend
|
.backend
|
||||||
.x11()
|
.x11()
|
||||||
.surfaces
|
.surfaces
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| &s.window == window)
|
.filter(|s| s.window.id() == window_id)
|
||||||
.map(|s| &s.output)
|
|
||||||
{
|
{
|
||||||
state.spaces.unmap_output(output);
|
surface.window.unmap();
|
||||||
|
state.spaces.unmap_output(&surface.output);
|
||||||
}
|
}
|
||||||
state.backend.x11().surfaces.retain(|s| &s.window != window);
|
|
||||||
}
|
|
||||||
X11Event::Resized(size) => {
|
|
||||||
let size = { (size.w as i32, size.h as i32).into() };
|
|
||||||
let mode = Mode {
|
|
||||||
size,
|
|
||||||
refresh: 60_000,
|
|
||||||
};
|
|
||||||
let surface = state
|
|
||||||
.backend
|
|
||||||
.x11()
|
|
||||||
.surfaces
|
|
||||||
.iter_mut()
|
|
||||||
.find(|s| &s.window == window)
|
|
||||||
.expect("Unmanaged X11 surface?");
|
|
||||||
surface.render.ping();
|
|
||||||
|
|
||||||
let output = &surface.output;
|
|
||||||
output.delete_mode(output.current_mode().unwrap());
|
|
||||||
output.change_current_state(Some(mode), None, None, None);
|
|
||||||
output.set_preferred(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
X11Event::PresentCompleted | X11Event::Refresh => {
|
|
||||||
state
|
state
|
||||||
|
.backend
|
||||||
|
.x11()
|
||||||
|
.surfaces
|
||||||
|
.retain(|s| s.window.id() != window_id);
|
||||||
|
}
|
||||||
|
X11Event::Resized {
|
||||||
|
new_size,
|
||||||
|
window_id,
|
||||||
|
} => {
|
||||||
|
let size = { (new_size.w as i32, new_size.h as i32).into() };
|
||||||
|
let mode = Mode {
|
||||||
|
size,
|
||||||
|
refresh: 60_000,
|
||||||
|
};
|
||||||
|
if let Some(surface) = state
|
||||||
.backend
|
.backend
|
||||||
.x11()
|
.x11()
|
||||||
.surfaces
|
.surfaces
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|s| &s.window == window)
|
.find(|s| s.window.id() == window_id)
|
||||||
.expect("Unmanaged X11 surface?")
|
{
|
||||||
.render
|
surface.render.ping();
|
||||||
.ping();
|
|
||||||
|
let output = &surface.output;
|
||||||
|
output.delete_mode(output.current_mode().unwrap());
|
||||||
|
output.change_current_state(Some(mode), None, None, None);
|
||||||
|
output.set_preferred(mode);
|
||||||
|
layer_map_for_output(output).arrange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
X11Event::Input(event) => { /*TODO*/ }
|
X11Event::PresentCompleted { window_id } | X11Event::Refresh { window_id } => {
|
||||||
|
if let Some(surface) = state
|
||||||
|
.backend
|
||||||
|
.x11()
|
||||||
|
.surfaces
|
||||||
|
.iter_mut()
|
||||||
|
.find(|s| s.window.id() == window_id)
|
||||||
|
{
|
||||||
|
surface.render.ping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Event::Input(event) => state.process_x11_event(event),
|
||||||
})
|
})
|
||||||
.map_err(|_| anyhow::anyhow!("Failed to insert X11 Backend into event loop"))?;
|
.map_err(|_| anyhow::anyhow!("Failed to insert X11 Backend into event loop"))?;
|
||||||
|
|
||||||
|
|
@ -285,3 +297,35 @@ fn init_egl_client_side(display: &mut Display, renderer: Rc<RefCell<Gles2Rendere
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn process_x11_event(&mut self, event: InputEvent<X11Input>) {
|
||||||
|
// here we can handle special cases for x11 inputs, like mapping them to windows
|
||||||
|
match &event {
|
||||||
|
InputEvent::PointerMotionAbsolute { event } => {
|
||||||
|
if let Some(window) = event.window() {
|
||||||
|
let output = self
|
||||||
|
.backend
|
||||||
|
.x11()
|
||||||
|
.surfaces
|
||||||
|
.iter()
|
||||||
|
.find(|surface| &surface.window == window.as_ref())
|
||||||
|
.map(|surface| surface.output.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let devices = seat.user_data().get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
set_active_output(seat, &output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.process_input_event(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
392
src/input/mod.rs
392
src/input/mod.rs
|
|
@ -3,19 +3,66 @@
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::{Device, DeviceCapability, InputBackend, InputEvent},
|
backend::input::{Device, DeviceCapability, InputBackend, InputEvent},
|
||||||
reexports::wayland_server::Display,
|
desktop::{layer_map_for_output, Space},
|
||||||
|
reexports::wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||||
|
utils::{Logical, Point},
|
||||||
wayland::{
|
wayland::{
|
||||||
data_device::set_data_device_focus,
|
data_device::set_data_device_focus,
|
||||||
output::Output,
|
output::Output,
|
||||||
seat::{CursorImageStatus, Keysym, Seat, XkbConfig},
|
seat::{CursorImageStatus, FilterResult, Keysym, Seat, XkbConfig},
|
||||||
|
shell::wlr_layer::Layer as WlrLayer,
|
||||||
|
SERIAL_COUNTER,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
pub struct ActiveOutput(pub RefCell<Output>);
|
pub struct ActiveOutput(pub RefCell<Output>);
|
||||||
|
|
||||||
|
pub struct Devices(RefCell<HashMap<String, Vec<DeviceCapability>>>);
|
||||||
|
|
||||||
|
impl Devices {
|
||||||
|
fn new() -> Devices {
|
||||||
|
Devices(RefCell::new(HashMap::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
|
||||||
|
let id = device.id();
|
||||||
|
let mut map = self.0.borrow_mut();
|
||||||
|
let caps = [DeviceCapability::Keyboard, DeviceCapability::Pointer]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.filter(|c| device.has_capability(*c))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let new_caps = caps
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
map.insert(id, caps);
|
||||||
|
new_caps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_device<D: Device>(&self, device: &D) -> bool {
|
||||||
|
self.0.borrow().contains_key(&device.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
|
||||||
|
let id = device.id();
|
||||||
|
let mut map = self.0.borrow_mut();
|
||||||
|
map.remove(&id)
|
||||||
|
.unwrap_or(Vec::new())
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| map.values().flatten().all(|has| *c != *has))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_seat(display: &mut Display, name: String) -> Seat {
|
pub fn add_seat(display: &mut Display, name: String) -> Seat {
|
||||||
let (seat, _) = Seat::new(display, name, None);
|
let (seat, _) = Seat::new(display, name, None);
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
userdata.insert_if_missing(|| Devices::new());
|
||||||
|
userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::Hidden));
|
||||||
|
userdata.insert_if_missing(|| Vec::<Keysym>::new());
|
||||||
seat
|
seat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,3 +79,344 @@ pub fn active_output(seat: &Seat, state: &State) -> Output {
|
||||||
.expect("Backend has no outputs?")
|
.expect("Backend has no outputs?")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_active_output(seat: &Seat, output: &Output) {
|
||||||
|
if !seat
|
||||||
|
.user_data()
|
||||||
|
.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone())))
|
||||||
|
{
|
||||||
|
*seat
|
||||||
|
.user_data()
|
||||||
|
.get::<ActiveOutput>()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.borrow_mut() = output.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||||
|
use smithay::backend::input::Event;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
InputEvent::DeviceAdded { device } => {
|
||||||
|
let seat = &mut self.last_active_seat;
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
for cap in devices.add_device(&device) {
|
||||||
|
match cap {
|
||||||
|
DeviceCapability::Keyboard => {
|
||||||
|
let _ =
|
||||||
|
seat.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
|
||||||
|
set_data_device_focus(
|
||||||
|
seat,
|
||||||
|
focus.and_then(|s| s.as_ref().client()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DeviceCapability::Pointer => {
|
||||||
|
let output = self
|
||||||
|
.spaces
|
||||||
|
.outputs()
|
||||||
|
.next()
|
||||||
|
.expect("Backend initialized without output")
|
||||||
|
.clone();
|
||||||
|
seat.user_data()
|
||||||
|
.insert_if_missing(|| ActiveOutput(RefCell::new(output)));
|
||||||
|
let owned_seat = seat.clone();
|
||||||
|
seat.add_pointer(move |status| {
|
||||||
|
*owned_seat
|
||||||
|
.user_data()
|
||||||
|
.get::<RefCell<CursorImageStatus>>()
|
||||||
|
.unwrap()
|
||||||
|
.borrow_mut() = status;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::DeviceRemoved { device } => {
|
||||||
|
for seat in &mut self.seats {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
for cap in devices.remove_device(&device) {
|
||||||
|
match cap {
|
||||||
|
DeviceCapability::Keyboard => {
|
||||||
|
seat.remove_keyboard();
|
||||||
|
}
|
||||||
|
DeviceCapability::Pointer => {
|
||||||
|
seat.remove_pointer();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::Keyboard { event, .. } => {
|
||||||
|
use smithay::backend::input::KeyboardKeyEvent;
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
let keycode = event.key_code();
|
||||||
|
let state = event.state();
|
||||||
|
slog_scope::trace!("key"; "keycode" => keycode, "state" => format!("{:?}", state));
|
||||||
|
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
let time = Event::time(&event);
|
||||||
|
seat.get_keyboard().unwrap().input::<(), _>(
|
||||||
|
keycode,
|
||||||
|
state,
|
||||||
|
serial,
|
||||||
|
time,
|
||||||
|
|modifiers, handle| {
|
||||||
|
// here we can handle global shortcuts and the like
|
||||||
|
let _ = (modifiers, handle);
|
||||||
|
FilterResult::Forward
|
||||||
|
},
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::PointerMotion { event, .. } => {
|
||||||
|
use smithay::backend::input::PointerMotionEvent;
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
let current_output = active_output(seat, &self);
|
||||||
|
|
||||||
|
let mut position = seat.get_pointer().unwrap().current_location();
|
||||||
|
position += event.delta();
|
||||||
|
|
||||||
|
let output = self
|
||||||
|
.spaces
|
||||||
|
.outputs()
|
||||||
|
.find(|output| {
|
||||||
|
self.spaces
|
||||||
|
.output_geometry(output)
|
||||||
|
.to_f64()
|
||||||
|
.contains(position)
|
||||||
|
})
|
||||||
|
.unwrap_or(¤t_output);
|
||||||
|
if output != ¤t_output {
|
||||||
|
set_active_output(seat, output);
|
||||||
|
}
|
||||||
|
let output_geometry = self.spaces.output_geometry(output);
|
||||||
|
|
||||||
|
position.x = 0.0f64
|
||||||
|
.max(position.x)
|
||||||
|
.min((output_geometry.loc.x + output_geometry.size.w) as f64);
|
||||||
|
position.y = 0.0f64
|
||||||
|
.max(position.y)
|
||||||
|
.min((output_geometry.loc.y + output_geometry.size.h) as f64);
|
||||||
|
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
let space = self.spaces.active_space(&output);
|
||||||
|
let under = State::surface_under(position, output, space);
|
||||||
|
seat.get_pointer()
|
||||||
|
.unwrap()
|
||||||
|
.motion(position, under, serial, event.time());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::PointerMotionAbsolute { event, .. } => {
|
||||||
|
use smithay::backend::input::PointerMotionAbsoluteEvent;
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
let output = active_output(seat, &self);
|
||||||
|
let space = self.spaces.active_space(&output);
|
||||||
|
let geometry = self.spaces.output_geometry(&output);
|
||||||
|
let position =
|
||||||
|
geometry.loc.to_f64() + event.position_transformed(geometry.size);
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
let under = State::surface_under(position, &output, space);
|
||||||
|
seat.get_pointer()
|
||||||
|
.unwrap()
|
||||||
|
.motion(position, under, serial, event.time());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::PointerButton { event, .. } => {
|
||||||
|
use smithay::{
|
||||||
|
backend::input::{ButtonState, PointerButtonEvent},
|
||||||
|
reexports::wayland_server::protocol::wl_pointer,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
let button = event.button_code();
|
||||||
|
let state = match event.state() {
|
||||||
|
ButtonState::Pressed => {
|
||||||
|
// change the keyboard focus unless the pointer is grabbed
|
||||||
|
if !seat.get_pointer().unwrap().is_grabbed() {
|
||||||
|
let output = active_output(seat, &self);
|
||||||
|
let mut pos = seat.get_pointer().unwrap().current_location();
|
||||||
|
let output_geo = self.spaces.output_geometry(&output);
|
||||||
|
let space = self.spaces.active_space_mut(&output);
|
||||||
|
let layers = layer_map_for_output(&output);
|
||||||
|
pos -= output_geo.loc.to_f64();
|
||||||
|
let mut under = None;
|
||||||
|
|
||||||
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Overlay, pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Top, pos))
|
||||||
|
{
|
||||||
|
if layer.can_receive_keyboard_focus() {
|
||||||
|
let layer_loc = layers.layer_geometry(layer).loc;
|
||||||
|
under = layer
|
||||||
|
.surface_under(pos - layer_loc.to_f64())
|
||||||
|
.map(|(s, _)| s);
|
||||||
|
}
|
||||||
|
} else if let Some(window) = space.window_under(pos).cloned() {
|
||||||
|
let window_loc =
|
||||||
|
space.window_geometry(&window).unwrap().loc;
|
||||||
|
under = window
|
||||||
|
.surface_under(pos - window_loc.to_f64())
|
||||||
|
.map(|(s, _)| s);
|
||||||
|
space.raise_window(&window);
|
||||||
|
} else if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Bottom, pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
|
||||||
|
{
|
||||||
|
if layer.can_receive_keyboard_focus() {
|
||||||
|
let layer_loc = layers.layer_geometry(layer).loc;
|
||||||
|
under = layer
|
||||||
|
.surface_under(pos - layer_loc.to_f64())
|
||||||
|
.map(|(s, _)| s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(keyboard) = seat.get_keyboard() {
|
||||||
|
keyboard.set_focus(under.as_ref(), serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wl_pointer::ButtonState::Pressed
|
||||||
|
}
|
||||||
|
ButtonState::Released => wl_pointer::ButtonState::Released,
|
||||||
|
};
|
||||||
|
seat.get_pointer()
|
||||||
|
.unwrap()
|
||||||
|
.button(button, state, serial, event.time());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputEvent::PointerAxis { event, .. } => {
|
||||||
|
use smithay::{
|
||||||
|
backend::input::{Axis, AxisSource, PointerAxisEvent},
|
||||||
|
reexports::wayland_server::protocol::wl_pointer,
|
||||||
|
wayland::seat::AxisFrame,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device = event.device();
|
||||||
|
for seat in self.seats.clone().iter() {
|
||||||
|
let userdata = seat.user_data();
|
||||||
|
let devices = userdata.get::<Devices>().unwrap();
|
||||||
|
if devices.has_device(&device) {
|
||||||
|
let source = match event.source() {
|
||||||
|
AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
|
||||||
|
AxisSource::Finger => wl_pointer::AxisSource::Finger,
|
||||||
|
AxisSource::Wheel | AxisSource::WheelTilt => {
|
||||||
|
wl_pointer::AxisSource::Wheel
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let horizontal_amount =
|
||||||
|
event.amount(Axis::Horizontal).unwrap_or_else(|| {
|
||||||
|
event.amount_discrete(Axis::Horizontal).unwrap() * 3.0
|
||||||
|
});
|
||||||
|
let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
|
||||||
|
event.amount_discrete(Axis::Vertical).unwrap() * 3.0
|
||||||
|
});
|
||||||
|
let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal);
|
||||||
|
let vertical_amount_discrete = event.amount_discrete(Axis::Vertical);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut frame = AxisFrame::new(event.time()).source(source);
|
||||||
|
if horizontal_amount != 0.0 {
|
||||||
|
frame = frame
|
||||||
|
.value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
|
||||||
|
if let Some(discrete) = horizontal_amount_discrete {
|
||||||
|
frame = frame.discrete(
|
||||||
|
wl_pointer::Axis::HorizontalScroll,
|
||||||
|
discrete as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if source == wl_pointer::AxisSource::Finger {
|
||||||
|
frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
|
||||||
|
}
|
||||||
|
if vertical_amount != 0.0 {
|
||||||
|
frame =
|
||||||
|
frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
|
||||||
|
if let Some(discrete) = vertical_amount_discrete {
|
||||||
|
frame = frame.discrete(
|
||||||
|
wl_pointer::Axis::VerticalScroll,
|
||||||
|
discrete as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if source == wl_pointer::AxisSource::Finger {
|
||||||
|
frame = frame.stop(wl_pointer::Axis::VerticalScroll);
|
||||||
|
}
|
||||||
|
seat.get_pointer().unwrap().axis(frame);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { /* TODO e.g. tablet or touch events */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface_under(
|
||||||
|
pos: Point<f64, Logical>,
|
||||||
|
output: &Output,
|
||||||
|
space: &Space,
|
||||||
|
) -> Option<(WlSurface, Point<i32, Logical>)> {
|
||||||
|
let layers = layer_map_for_output(output);
|
||||||
|
let output_geo = space.output_geometry(output).unwrap();
|
||||||
|
|
||||||
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Overlay, pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Top, pos))
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).loc;
|
||||||
|
layer
|
||||||
|
.surface_under(pos - output_geo.loc.to_f64() - layer_loc.to_f64())
|
||||||
|
.map(|(s, loc)| (s, loc + layer_loc))
|
||||||
|
} else if let Some(window) = space.window_under(pos) {
|
||||||
|
let window_loc = space.window_geometry(window).unwrap().loc;
|
||||||
|
window
|
||||||
|
.surface_under(pos - window_loc.to_f64())
|
||||||
|
.map(|(s, loc)| (s, loc + window_loc))
|
||||||
|
} else if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Bottom, pos)
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).loc;
|
||||||
|
layer
|
||||||
|
.surface_under(pos - output_geo.loc.to_f64() - layer_loc.to_f64())
|
||||||
|
.map(|(s, loc)| (s, loc + layer_loc))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use anyhow::{Context, Result};
|
||||||
use slog::Drain;
|
use slog::Drain;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
pub mod input;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
pub use smithay::{
|
pub use smithay::{
|
||||||
desktop::Space, reexports::wayland_server::protocol::wl_surface::WlSurface,
|
desktop::Space,
|
||||||
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
|
utils::{Logical, Rectangle, Size},
|
||||||
wayland::output::Output,
|
wayland::output::Output,
|
||||||
};
|
};
|
||||||
use std::{cell::Cell, mem::MaybeUninit};
|
use std::{cell::Cell, mem::MaybeUninit};
|
||||||
|
|
@ -115,6 +117,31 @@ impl Workspaces {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn output_size(&self, output: &Output) -> Size<i32, Logical> {
|
||||||
|
let space = self.active_space(output);
|
||||||
|
space
|
||||||
|
.output_geometry(&output)
|
||||||
|
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)))
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn output_geometry(&self, output: &Output) -> Rectangle<i32, Logical> {
|
||||||
|
// due to our different modes, we cannot just ask the space for the global output coordinates,
|
||||||
|
// because for `Mode::OutputBound` the origin will always be (0, 0)
|
||||||
|
|
||||||
|
// TODO: Add a proper grid like structure, for now the outputs just extend to the right
|
||||||
|
let pos =
|
||||||
|
self.outputs
|
||||||
|
.iter()
|
||||||
|
.take_while(|o| o != &output)
|
||||||
|
.fold((0, 0), |(x, y), output| {
|
||||||
|
let size = self.output_size(output);
|
||||||
|
(x + size.w, y)
|
||||||
|
});
|
||||||
|
|
||||||
|
Rectangle::from_loc_and_size(pos, self.output_size(output))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn activate(&mut self, output: &Output, idx: usize) {
|
pub fn activate(&mut self, output: &Output, idx: usize) {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::OutputBound => {
|
Mode::OutputBound => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue