mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-18 22:26:12 +01:00
Copied Anvil's udev support
This commit is contained in:
parent
65760b2434
commit
748dadae01
12 changed files with 2308 additions and 557 deletions
22
Cargo.toml
22
Cargo.toml
|
@ -11,4 +11,24 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
|||
smithay = { git = "https://github.com/Smithay/smithay" }
|
||||
smithay-drm-extras = { git = "https://github.com/Smithay/smithay", optional = true }
|
||||
thiserror = "1.0.40"
|
||||
xcursor = { version = "0.3.4", optional = true }
|
||||
xcursor = {version = "0.3.4", optional = true }
|
||||
image = {version = "0.24.0", default-features = false, optional = true}
|
||||
|
||||
[features]
|
||||
default = ["egl", "winit", "udev"]
|
||||
egl = ["smithay/use_system_lib", "smithay/backend_egl"]
|
||||
udev = [
|
||||
"smithay-drm-extras",
|
||||
"smithay/backend_libinput",
|
||||
"smithay/backend_udev",
|
||||
"smithay/backend_drm",
|
||||
"smithay/backend_gbm",
|
||||
"smithay/backend_vulkan",
|
||||
"smithay/backend_egl",
|
||||
"smithay/backend_session_libseat",
|
||||
"image",
|
||||
"smithay/renderer_gl",
|
||||
"smithay/renderer_multi",
|
||||
"xcursor",
|
||||
]
|
||||
winit = ["smithay/backend_winit", "smithay/backend_drm"]
|
||||
|
|
BIN
resources/cursor.rgba
Normal file
BIN
resources/cursor.rgba
Normal file
Binary file not shown.
BIN
resources/numbers.png
Normal file
BIN
resources/numbers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -1,4 +1,8 @@
|
|||
use smithay::{output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface};
|
||||
use smithay::{
|
||||
backend::input::{InputBackend, InputEvent},
|
||||
output::Output,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
};
|
||||
|
||||
pub mod udev;
|
||||
pub mod winit;
|
||||
|
|
1525
src/backend/udev.rs
1525
src/backend/udev.rs
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,4 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
os::fd::AsRawFd,
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{error::Error, sync::Mutex, time::Duration};
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
@ -24,42 +19,31 @@ use smithay::{
|
|||
desktop::{
|
||||
space,
|
||||
utils::{surface_primary_scanout_output, update_surface_primary_scanout_output},
|
||||
PopupManager, Space, Window,
|
||||
},
|
||||
input::{
|
||||
pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
SeatState,
|
||||
},
|
||||
input::pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
output::{Output, Subpixel},
|
||||
reexports::{
|
||||
calloop::{
|
||||
generic::Generic,
|
||||
timer::{TimeoutAction, Timer},
|
||||
EventLoop, Interest, Mode, PostAction,
|
||||
EventLoop,
|
||||
},
|
||||
wayland_server::{protocol::wl_surface::WlSurface, Display},
|
||||
},
|
||||
utils::{Clock, IsAlive, Monotonic, Physical, Point, Scale, Transform},
|
||||
utils::{IsAlive, Scale, Transform},
|
||||
wayland::{
|
||||
compositor::{self, CompositorState},
|
||||
data_device::DataDeviceState,
|
||||
compositor::{self},
|
||||
dmabuf::{
|
||||
DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
ImportError,
|
||||
},
|
||||
fractional_scale::{with_fractional_scale, FractionalScaleManagerState},
|
||||
output::OutputManagerState,
|
||||
shell::xdg::XdgShellState,
|
||||
shm::ShmState,
|
||||
socket::ListeningSocketSource,
|
||||
viewporter::ViewporterState,
|
||||
fractional_scale::with_fractional_scale,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
layout::{Direction, Layout},
|
||||
render::{pointer::PointerElement, CustomRenderElements, OutputRenderElements},
|
||||
state::{CalloopData, ClientState, State},
|
||||
state::{CalloopData, State},
|
||||
};
|
||||
|
||||
use super::Backend;
|
||||
|
@ -104,41 +88,12 @@ impl DmabufHandler for State<WinitData> {
|
|||
delegate_dmabuf!(State<WinitData>);
|
||||
|
||||
pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
||||
let mut event_loop: EventLoop<CalloopData> = EventLoop::try_new()?;
|
||||
let mut event_loop: EventLoop<CalloopData<WinitData>> = EventLoop::try_new()?;
|
||||
|
||||
let mut display: Display<State<WinitData>> = Display::new()?;
|
||||
|
||||
let socket = ListeningSocketSource::new_auto()?;
|
||||
let socket_name = socket.socket_name().to_os_string();
|
||||
|
||||
let evt_loop_handle = event_loop.handle();
|
||||
|
||||
evt_loop_handle.insert_source(socket, |stream, _metadata, data| {
|
||||
data.display
|
||||
.handle()
|
||||
.insert_client(stream, Arc::new(ClientState::default()))
|
||||
.unwrap();
|
||||
})?;
|
||||
|
||||
evt_loop_handle.insert_source(
|
||||
Generic::new(
|
||||
display.backend().poll_fd().as_raw_fd(),
|
||||
Interest::READ,
|
||||
Mode::Level,
|
||||
),
|
||||
|_readiness, _metadata, data| {
|
||||
data.display.dispatch_clients(&mut data.state)?;
|
||||
Ok(PostAction::Continue)
|
||||
},
|
||||
)?;
|
||||
|
||||
let display_handle = display.handle();
|
||||
|
||||
let mut seat_state = SeatState::<State<WinitData>>::new();
|
||||
let mut seat = seat_state.new_wl_seat(&display_handle, "seat1");
|
||||
|
||||
seat.add_keyboard(Default::default(), 500, 50)?;
|
||||
seat.add_pointer();
|
||||
let evt_loop_handle = event_loop.handle();
|
||||
|
||||
let (mut winit_backend, mut winit_evt_loop) = smithay::backend::winit::init::<GlesRenderer>()?;
|
||||
|
||||
|
@ -214,39 +169,17 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
};
|
||||
|
||||
let mut state = State {
|
||||
// TODO: move winit_backend and damage_tracker into their own scope so I can't access them
|
||||
// | from after this
|
||||
backend_data: WinitData {
|
||||
let mut state = State::init(
|
||||
WinitData {
|
||||
backend: winit_backend,
|
||||
damage_tracker: OutputDamageTracker::from_output(&output),
|
||||
dmabuf_state,
|
||||
full_redraw: 0,
|
||||
},
|
||||
loop_signal: event_loop.get_signal(),
|
||||
loop_handle: event_loop.handle(),
|
||||
clock: Clock::<Monotonic>::new()?,
|
||||
compositor_state: CompositorState::new::<State<WinitData>>(&display_handle),
|
||||
data_device_state: DataDeviceState::new::<State<WinitData>>(&display_handle),
|
||||
seat_state,
|
||||
pointer_location: (0.0, 0.0).into(),
|
||||
shm_state: ShmState::new::<State<WinitData>>(&display_handle, vec![]),
|
||||
space: Space::<Window>::default(),
|
||||
cursor_status: CursorImageStatus::Default,
|
||||
output_manager_state: OutputManagerState::new_with_xdg_output::<State<WinitData>>(
|
||||
&display_handle,
|
||||
),
|
||||
xdg_shell_state: XdgShellState::new::<State<WinitData>>(&display_handle),
|
||||
viewporter_state: ViewporterState::new::<State<WinitData>>(&display_handle),
|
||||
fractional_scale_manager_state: FractionalScaleManagerState::new::<State<WinitData>>(
|
||||
&display_handle,
|
||||
),
|
||||
|
||||
move_mode: false,
|
||||
socket_name: socket_name.clone(),
|
||||
|
||||
popup_manager: PopupManager::default(),
|
||||
};
|
||||
&mut display,
|
||||
event_loop.get_signal(),
|
||||
evt_loop_handle,
|
||||
)?;
|
||||
|
||||
state
|
||||
.shm_state
|
||||
|
@ -254,194 +187,191 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
state.space.map_output(&output, (0, 0));
|
||||
|
||||
std::env::set_var("WAYLAND_DISPLAY", socket_name);
|
||||
|
||||
let mut pointer_element = PointerElement::<GlesTexture>::new();
|
||||
|
||||
// TODO: pointer
|
||||
evt_loop_handle.insert_source(Timer::immediate(), move |_instant, _metadata, data| {
|
||||
let display = &mut data.display;
|
||||
let state = &mut data.state;
|
||||
state
|
||||
.loop_handle
|
||||
.insert_source(Timer::immediate(), move |_instant, _metadata, data| {
|
||||
let display = &mut data.display;
|
||||
let state = &mut data.state;
|
||||
|
||||
let result = winit_evt_loop.dispatch_new_events(|event| match event {
|
||||
WinitEvent::Resized {
|
||||
size,
|
||||
scale_factor: _,
|
||||
} => {
|
||||
output.change_current_state(
|
||||
Some(smithay::output::Mode {
|
||||
size,
|
||||
refresh: 144_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Layout::master_stack(
|
||||
state,
|
||||
state.space.elements().cloned().collect(),
|
||||
Direction::Left,
|
||||
);
|
||||
}
|
||||
WinitEvent::Focus(_) => {}
|
||||
WinitEvent::Input(input_evt) => {
|
||||
state.process_input_event(&seat, input_evt);
|
||||
}
|
||||
WinitEvent::Refresh => {}
|
||||
});
|
||||
let result = winit_evt_loop.dispatch_new_events(|event| match event {
|
||||
WinitEvent::Resized {
|
||||
size,
|
||||
scale_factor: _,
|
||||
} => {
|
||||
output.change_current_state(
|
||||
Some(smithay::output::Mode {
|
||||
size,
|
||||
refresh: 144_000,
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Layout::master_stack(
|
||||
state,
|
||||
state.space.elements().cloned().collect(),
|
||||
Direction::Left,
|
||||
);
|
||||
}
|
||||
WinitEvent::Focus(_) => {}
|
||||
WinitEvent::Input(input_evt) => {
|
||||
state.process_input_event(input_evt);
|
||||
}
|
||||
WinitEvent::Refresh => {}
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(WinitError::WindowClosed) => {
|
||||
state.loop_signal.stop();
|
||||
}
|
||||
};
|
||||
|
||||
if let CursorImageStatus::Surface(ref surface) = state.cursor_status {
|
||||
if !surface.alive() {
|
||||
state.cursor_status = CursorImageStatus::Default;
|
||||
}
|
||||
}
|
||||
|
||||
let cursor_visible = !matches!(state.cursor_status, CursorImageStatus::Surface(_));
|
||||
|
||||
pointer_element.set_status(state.cursor_status.clone());
|
||||
|
||||
let full_redraw = &mut state.backend_data.full_redraw;
|
||||
*full_redraw = full_redraw.saturating_sub(1);
|
||||
|
||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||
let cursor_hotspot = if let CursorImageStatus::Surface(ref surface) = state.cursor_status {
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot
|
||||
})
|
||||
} else {
|
||||
(0, 0).into()
|
||||
};
|
||||
let cursor_pos = state.pointer_location - cursor_hotspot.to_f64();
|
||||
let cursor_pos_scaled = cursor_pos.to_physical(scale).to_i32_round::<i32>();
|
||||
|
||||
let mut custom_render_elements = Vec::<CustomRenderElements<GlesRenderer>>::new();
|
||||
|
||||
custom_render_elements.extend(pointer_element.render_elements(
|
||||
state.backend_data.backend.renderer(),
|
||||
cursor_pos_scaled,
|
||||
scale,
|
||||
1.0,
|
||||
));
|
||||
|
||||
tracing::info!(
|
||||
"custom_render_elements len = {}",
|
||||
custom_render_elements.len()
|
||||
);
|
||||
|
||||
let render_res = state.backend_data.backend.bind().and_then(|_| {
|
||||
let age = if *full_redraw > 0 {
|
||||
0
|
||||
} else {
|
||||
state.backend_data.backend.buffer_age().unwrap_or(0)
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(WinitError::WindowClosed) => {
|
||||
state.loop_signal.stop();
|
||||
}
|
||||
};
|
||||
|
||||
let renderer = state.backend_data.backend.renderer();
|
||||
|
||||
// render_output()
|
||||
let space_render_elements =
|
||||
space::space_render_elements(renderer, [&state.space], &output, 1.0).unwrap();
|
||||
|
||||
let mut output_render_elements = Vec::<
|
||||
OutputRenderElements<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>,
|
||||
>::new();
|
||||
|
||||
output_render_elements.extend(
|
||||
custom_render_elements
|
||||
.into_iter()
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
output_render_elements.extend(
|
||||
space_render_elements
|
||||
.into_iter()
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
|
||||
state
|
||||
.backend_data
|
||||
.damage_tracker
|
||||
.render_output(renderer, age, &output_render_elements, [0.5, 0.5, 0.5, 1.0])
|
||||
.map_err(|err| match err {
|
||||
damage::Error::Rendering(err) => err.into(),
|
||||
damage::Error::OutputNoMode(_) => todo!(),
|
||||
})
|
||||
});
|
||||
|
||||
match render_res {
|
||||
Ok((damage, states)) => {
|
||||
let has_rendered = damage.is_some();
|
||||
if let Some(damage) = damage {
|
||||
if let Err(err) = state.backend_data.backend.submit(Some(&damage)) {
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
if let CursorImageStatus::Surface(ref surface) = state.cursor_status {
|
||||
if !surface.alive() {
|
||||
state.cursor_status = CursorImageStatus::Default;
|
||||
}
|
||||
}
|
||||
|
||||
let cursor_visible = !matches!(state.cursor_status, CursorImageStatus::Surface(_));
|
||||
|
||||
pointer_element.set_status(state.cursor_status.clone());
|
||||
|
||||
let full_redraw = &mut state.backend_data.full_redraw;
|
||||
*full_redraw = full_redraw.saturating_sub(1);
|
||||
|
||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||
let cursor_hotspot =
|
||||
if let CursorImageStatus::Surface(ref surface) = state.cursor_status {
|
||||
compositor::with_states(surface, |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<CursorImageAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.hotspot
|
||||
})
|
||||
} else {
|
||||
(0, 0).into()
|
||||
};
|
||||
let cursor_pos = state.pointer_location - cursor_hotspot.to_f64();
|
||||
let cursor_pos_scaled = cursor_pos.to_physical(scale).to_i32_round::<i32>();
|
||||
|
||||
let mut custom_render_elements = Vec::<CustomRenderElements<GlesRenderer>>::new();
|
||||
|
||||
custom_render_elements.extend(pointer_element.render_elements(
|
||||
state.backend_data.backend.renderer(),
|
||||
cursor_pos_scaled,
|
||||
scale,
|
||||
1.0,
|
||||
));
|
||||
|
||||
let render_res = state.backend_data.backend.bind().and_then(|_| {
|
||||
let age = if *full_redraw > 0 {
|
||||
0
|
||||
} else {
|
||||
state.backend_data.backend.buffer_age().unwrap_or(0)
|
||||
};
|
||||
|
||||
let renderer = state.backend_data.backend.renderer();
|
||||
|
||||
// render_output()
|
||||
let space_render_elements =
|
||||
space::space_render_elements(renderer, [&state.space], &output, 1.0).unwrap();
|
||||
|
||||
let mut output_render_elements = Vec::<
|
||||
OutputRenderElements<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>,
|
||||
>::new();
|
||||
|
||||
output_render_elements.extend(
|
||||
custom_render_elements
|
||||
.into_iter()
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
output_render_elements.extend(
|
||||
space_render_elements
|
||||
.into_iter()
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
|
||||
state
|
||||
.backend_data
|
||||
.backend
|
||||
.window()
|
||||
.set_cursor_visible(cursor_visible);
|
||||
.damage_tracker
|
||||
.render_output(renderer, age, &output_render_elements, [0.5, 0.5, 0.5, 1.0])
|
||||
.map_err(|err| match err {
|
||||
damage::Error::Rendering(err) => err.into(),
|
||||
damage::Error::OutputNoMode(_) => todo!(),
|
||||
})
|
||||
});
|
||||
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
match render_res {
|
||||
Ok((damage, states)) => {
|
||||
let has_rendered = damage.is_some();
|
||||
if let Some(damage) = damage {
|
||||
if let Err(err) = state.backend_data.backend.submit(Some(&damage)) {
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
}
|
||||
|
||||
state.space.elements().for_each(|window| {
|
||||
window.with_surfaces(|surface, states_inner| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
&output,
|
||||
states_inner,
|
||||
&states,
|
||||
default_primary_scanout_output_compare,
|
||||
);
|
||||
state
|
||||
.backend_data
|
||||
.backend
|
||||
.window()
|
||||
.set_cursor_visible(cursor_visible);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states_inner, |fraction_scale| {
|
||||
fraction_scale
|
||||
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||
});
|
||||
let throttle = Some(Duration::from_secs(1));
|
||||
|
||||
state.space.elements().for_each(|window| {
|
||||
window.with_surfaces(|surface, states_inner| {
|
||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||
surface,
|
||||
&output,
|
||||
states_inner,
|
||||
&states,
|
||||
default_primary_scanout_output_compare,
|
||||
);
|
||||
|
||||
if let Some(output) = primary_scanout_output {
|
||||
with_fractional_scale(states_inner, |fraction_scale| {
|
||||
fraction_scale.set_preferred_scale(
|
||||
output.current_scale().fractional_scale(),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if state.space.outputs_for_element(window).contains(&output) {
|
||||
window.send_frame(
|
||||
&output,
|
||||
state.clock.now(),
|
||||
throttle,
|
||||
surface_primary_scanout_output,
|
||||
);
|
||||
// TODO: dmabuf_feedback
|
||||
}
|
||||
});
|
||||
|
||||
if state.space.outputs_for_element(window).contains(&output) {
|
||||
window.send_frame(
|
||||
&output,
|
||||
state.clock.now(),
|
||||
throttle,
|
||||
surface_primary_scanout_output,
|
||||
);
|
||||
// TODO: dmabuf_feedback
|
||||
if has_rendered {
|
||||
// TODO:
|
||||
}
|
||||
});
|
||||
|
||||
if has_rendered {
|
||||
// TODO:
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
}
|
||||
|
||||
state.space.refresh();
|
||||
state.popup_manager.cleanup();
|
||||
display
|
||||
.flush_clients()
|
||||
.expect("failed to flush client buffers");
|
||||
state.space.refresh();
|
||||
state.popup_manager.cleanup();
|
||||
display
|
||||
.flush_clients()
|
||||
.expect("failed to flush client buffers");
|
||||
|
||||
TimeoutAction::ToDuration(Duration::from_millis(6))
|
||||
})?;
|
||||
TimeoutAction::ToDuration(Duration::from_millis(6))
|
||||
})?;
|
||||
|
||||
event_loop.run(None, &mut CalloopData { display, state }, |_data| {})?;
|
||||
|
||||
|
|
89
src/cursor.rs
Normal file
89
src/cursor.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use std::{io::Read, time::Duration};
|
||||
|
||||
use xcursor::{parser::Image, CursorTheme};
|
||||
|
||||
static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../resources/cursor.rgba");
|
||||
|
||||
pub struct Cursor {
|
||||
icons: Vec<Image>,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
pub fn load() -> Self {
|
||||
let name = std::env::var("XCURSOR_THEME")
|
||||
.ok()
|
||||
.unwrap_or_else(|| "default".into());
|
||||
let size = std::env::var("XCURSOR_SIZE")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(24);
|
||||
|
||||
let theme = CursorTheme::load(&name);
|
||||
let icons = load_icon(&theme)
|
||||
.map_err(|err| tracing::warn!("Unable to load xcursor: {}, using fallback cursor", err))
|
||||
.unwrap_or_else(|_| {
|
||||
vec![Image {
|
||||
size: 32,
|
||||
width: 64,
|
||||
height: 64,
|
||||
xhot: 1,
|
||||
yhot: 1,
|
||||
delay: 1,
|
||||
pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA),
|
||||
pixels_argb: vec![], //unused
|
||||
}]
|
||||
});
|
||||
|
||||
Cursor { icons, size }
|
||||
}
|
||||
|
||||
pub fn get_image(&self, scale: u32, time: Duration) -> Image {
|
||||
let size = self.size * scale;
|
||||
frame(time.as_millis() as u32, size, &self.icons)
|
||||
}
|
||||
}
|
||||
|
||||
fn nearest_images(size: u32, images: &[Image]) -> impl Iterator<Item = &Image> {
|
||||
// Follow the nominal size of the cursor to choose the nearest
|
||||
let nearest_image = images
|
||||
.iter()
|
||||
.min_by_key(|image| (size as i32 - image.size as i32).abs())
|
||||
.unwrap();
|
||||
|
||||
images.iter().filter(move |image| {
|
||||
image.width == nearest_image.width && image.height == nearest_image.height
|
||||
})
|
||||
}
|
||||
|
||||
fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image {
|
||||
let total = nearest_images(size, images).fold(0, |acc, image| acc + image.delay);
|
||||
millis %= total;
|
||||
|
||||
for img in nearest_images(size, images) {
|
||||
if millis < img.delay {
|
||||
return img.clone();
|
||||
}
|
||||
millis -= img.delay;
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
enum Error {
|
||||
#[error("Theme has no default cursor")]
|
||||
NoDefaultCursor,
|
||||
#[error("Error opening xcursor file: {0}")]
|
||||
File(#[from] std::io::Error),
|
||||
#[error("Failed to parse XCursor file")]
|
||||
Parse,
|
||||
}
|
||||
|
||||
fn load_icon(theme: &CursorTheme) -> Result<Vec<Image>, Error> {
|
||||
let icon_path = theme.load_icon("default").ok_or(Error::NoDefaultCursor)?;
|
||||
let mut cursor_file = std::fs::File::open(icon_path)?;
|
||||
let mut cursor_data = Vec::new();
|
||||
cursor_file.read_to_end(&mut cursor_data)?;
|
||||
xcursor::parser::parse_xcursor(&cursor_data).ok_or(Error::Parse)
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use smithay::{
|
||||
backend::renderer::utils,
|
||||
delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_output,
|
||||
delegate_seat, delegate_shm, delegate_viewporter, delegate_xdg_shell,
|
||||
delegate_presentation, delegate_relative_pointer, delegate_seat, delegate_shm,
|
||||
delegate_viewporter, delegate_xdg_shell,
|
||||
desktop::{
|
||||
find_popup_root_surface, PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab,
|
||||
PopupUngrabStrategy, Space, Window,
|
||||
|
@ -171,7 +172,7 @@ impl<B: Backend> SeatHandler for State<B> {
|
|||
}
|
||||
|
||||
fn cursor_image(&mut self, _seat: &Seat<Self>, image: CursorImageStatus) {
|
||||
tracing::info!("new cursor image: {:?}", image);
|
||||
// tracing::info!("new cursor image: {:?}", image);
|
||||
self.cursor_status = image;
|
||||
}
|
||||
|
||||
|
@ -305,27 +306,6 @@ delegate_viewporter!(@<B: Backend> State<B>);
|
|||
impl<B: Backend> FractionalScaleHandler for State<B> {
|
||||
fn new_fractional_scale(&mut self, surface: WlSurface) {
|
||||
// ripped straight from anvil
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣤⣴⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣦⣤⣤⣤⣀⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⢿⣟⣛⣭⡽⠶⠶⠶⠮⠭⠭⣭⣭⣭⣭⣭⣭⣭⣿⣿⣯⣭⣥⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⢟⣫⣶⠿⣫⣭⣶⠿⠿⣿⣿⣿⠿⢿⣷⣶⣮⣭⣭⣭⣭⣭⣷⣶⣶⣶⣾⣽⣿⣷⣦⡀⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣫⣾⣟⣩⣞⣫⣵⣿⣿⣿⣿⣿⣿⣿⣯⢻⣿⣿⣿⣿⣿⣿⢻⣿⣿⣿⣿⣶⣍⡻⣿⣿⣿⣷⠀⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⣿⣵⣿⡿⠿⠛⠛⠛⠛⠿⣿⣿⣿⡇⣿⣿⣿⣿⣿⣣⣿⣿⣿⣿⠿⢿⣿⣷⣼⣿⣿⣿⣇⠀⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⢀⣚⣯⣽⣿⣿⣿⣿⢻⣿⣏⡅⠀⠀⠀⠀⠀⠠⣿⣷⣯⡛⣿⣿⣿⣿⣿⣿⡿⠟⠉⠁⠐⣿⣿⣶⣽⣿⣟⣛⡻⠿⣦⡀⠀⠀
|
||||
// ⠀⢀⣴⣞⣯⣷⠶⣒⣛⣛⡻⢿⣷⣿⣷⣾⣶⣾⢟⣿⣿⣿⣶⣯⣟⣫⣿⣿⣿⣿⣿⣍⠀⣀⣤⣤⣬⣭⣽⣿⣿⣿⣿⣿⣿⣟⢶⡝⣦⠀
|
||||
// ⠀⣿⡿⣾⣿⣵⣿⣿⣿⣿⣿⣷⣾⣭⣽⣿⣭⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⡿⠿⢟⣫⣭⣭⣽⣿⣷⣿⢸⠀
|
||||
// ⠀⣿⡇⣿⣿⣿⡿⠿⢟⣴⣬⣛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⣿⣿⣷⣝⣛⢿⣿⣿⣿⣿⣿⣿⡟⣿⣿⣿⢟⣿⢸⠀
|
||||
// ⠀⢿⣧⣿⣿⣿⣿⣿⣧⢻⣿⣿⣿⣷⣮⢙⡻⠿⣿⣿⣯⣭⣾⡇⣿⣿⣟⣭⣻⣿⣿⣿⣿⣿⡿⣸⣿⠿⢿⣿⣿⡿⡁⢹⣿⣷⢿⣱⠇⠀
|
||||
// ⠀⠀⠻⢷⣝⣿⣿⣿⣿⣧⠉⠻⢿⣿⣿⢸⣿⣿⣷⣶⣭⣝⢛⠿⢿⣿⣿⣿⣿⣿⣯⣙⣛⣭⣾⣿⣿⣿⣿⠿⡋⣾⣿⡈⣿⣿⣿⡏⠀⠀
|
||||
// ⠀⠀⠀⠀⠸⣽⣿⣿⣿⣿⣷⡽⣿⣷⣆⢘⠿⣿⣿⣿⣿⣿⢸⣿⣿⣶⣶⣶⡎⣭⣭⣭⣭⡩⣭⣭⣽⣦⣰⣿⣧⢿⣿⡇⣿⣿⣿⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣮⣻⣏⣿⣿⣾⣯⣍⠛⠋⠻⢿⣿⣿⣿⣿⡇⣿⣿⣿⣿⡇⣿⣿⣿⣿⡟⣿⠟⠈⠉⠀⣿⣿⡏⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣾⡿⣿⣿⣿⣿⣿⢦⣴⣦⣬⣍⡛⠛⠈⠛⠛⠛⠛⠁⠙⠛⠛⠉⠀⠀⠀⠀⢠⡆⣿⣿⡇⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣮⣿⡻⣿⢏⣾⣿⣿⣿⣿⣿⣷⣶⣾⣷⣶⣄⣴⣶⣤⡤⣶⣶⡆⣾⡿⡸⣱⣿⣿⡇⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣷⣝⡻⢶⣽⣻⢿⣿⣷⣭⣝⣻⣿⡿⠿⠿⠏⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⡿⠱⣿⣃⣵⣿⣿⣿⣧⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⢿⣶⣝⡻⢷⣮⣝⡻⢿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⡿⣿⣿⣿⣿⣿⡄⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⢷⣮⣝⡻⢿⣷⣮⣭⣛⣻⠿⠿⣿⣶⣶⣶⣶⣿⣿⣿⠿⢿⣛⣽⣾⣿⡿⣹⣿⣿⡇⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⢿⣾⣶⣭⣽⣛⣛⠿⠿⠶⢶⣶⣶⣶⣶⡿⠿⠿⢟⣛⣭⣷⣿⣿⣿⣿⠇⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠻⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀
|
||||
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠛⠛⠛⠛⠿⠿⠛⠛⠛⠉⠁⠀⠀⠀⠀⠀
|
||||
|
||||
// Here we can set the initial fractional scale
|
||||
//
|
||||
|
@ -375,3 +355,7 @@ impl<B: Backend> FractionalScaleHandler for State<B> {
|
|||
}
|
||||
|
||||
delegate_fractional_scale!(@<B: Backend> State<B>);
|
||||
|
||||
delegate_relative_pointer!(@<B: Backend> State<B>);
|
||||
|
||||
delegate_presentation!(@<B: Backend> State<B>);
|
||||
|
|
626
src/input.rs
626
src/input.rs
|
@ -1,279 +1,395 @@
|
|||
use smithay::{
|
||||
backend::input::{
|
||||
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
|
||||
KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
|
||||
KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent,
|
||||
},
|
||||
desktop::WindowSurfaceType,
|
||||
input::{
|
||||
keyboard::{keysyms, FilterResult},
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent},
|
||||
Seat,
|
||||
pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent},
|
||||
},
|
||||
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||
utils::{Point, SERIAL_COUNTER},
|
||||
utils::{Logical, Point, SERIAL_COUNTER},
|
||||
};
|
||||
|
||||
use crate::{backend::winit::WinitData, state::State};
|
||||
use crate::{
|
||||
backend::{udev::UdevData, winit::WinitData, Backend},
|
||||
state::State,
|
||||
};
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
fn pointer_button<I: InputBackend>(&mut self, event: I::PointerButtonEvent) {
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
let keyboard = self.seat.get_keyboard().unwrap();
|
||||
|
||||
// A serial is a number sent with a event that is sent back to the
|
||||
// server by the clients in further requests. This allows the server to
|
||||
// keep track of which event caused which requests. It is an AtomicU32
|
||||
// that increments when next_serial is called.
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
// Returns which button on the pointer was used.
|
||||
let button = event.button_code();
|
||||
|
||||
// The state, either released or pressed.
|
||||
let button_state = event.state();
|
||||
|
||||
let pointer_loc = pointer.current_location();
|
||||
|
||||
// If the button was clicked, focus on the window below if exists, else
|
||||
// unfocus on windows.
|
||||
if ButtonState::Pressed == button_state {
|
||||
if let Some((window, window_loc)) = self
|
||||
.space
|
||||
.element_under(pointer_loc)
|
||||
.map(|(w, l)| (w.clone(), l))
|
||||
{
|
||||
const BUTTON_LEFT: u32 = 0x110;
|
||||
const BUTTON_RIGHT: u32 = 0x111;
|
||||
if self.move_mode {
|
||||
if event.button_code() == BUTTON_LEFT {
|
||||
crate::xdg::request::move_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
);
|
||||
return; // TODO: kinda ugly return here
|
||||
} else if event.button_code() == BUTTON_RIGHT {
|
||||
let window_geometry = window.geometry();
|
||||
let window_x = window_loc.x as f64;
|
||||
let window_y = window_loc.y as f64;
|
||||
let window_width = window_geometry.size.w as f64;
|
||||
let window_height = window_geometry.size.h as f64;
|
||||
let half_width = window_x + window_width / 2.0;
|
||||
let half_height = window_y + window_height / 2.0;
|
||||
let full_width = window_x + window_width;
|
||||
let full_height = window_y + window_height;
|
||||
|
||||
let edges = match pointer_loc {
|
||||
Point { x, y, .. }
|
||||
if (window_x..=half_width).contains(&x)
|
||||
&& (window_y..=half_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::TopLeft
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (half_width..=full_width).contains(&x)
|
||||
&& (window_y..=half_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::TopRight
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (window_x..=half_width).contains(&x)
|
||||
&& (half_height..=full_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::BottomLeft
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (half_width..=full_width).contains(&x)
|
||||
&& (half_height..=full_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::BottomRight
|
||||
}
|
||||
_ => ResizeEdge::None,
|
||||
};
|
||||
|
||||
crate::xdg::request::resize_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
&self.seat.clone(),
|
||||
serial,
|
||||
edges,
|
||||
BUTTON_RIGHT,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Move window to top of stack.
|
||||
self.space.raise_element(&window, true);
|
||||
|
||||
// Focus on window.
|
||||
keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial);
|
||||
self.space.elements().for_each(|window| {
|
||||
window.toplevel().send_configure();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.space.elements().for_each(|window| {
|
||||
window.set_activated(false);
|
||||
window.toplevel().send_configure();
|
||||
});
|
||||
keyboard.set_focus(self, None, serial);
|
||||
}
|
||||
};
|
||||
|
||||
// Send the button event to the client.
|
||||
pointer.button(
|
||||
self,
|
||||
&ButtonEvent {
|
||||
button,
|
||||
state: button_state,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn pointer_axis<I: InputBackend>(&mut self, event: I::PointerAxisEvent) {
|
||||
let source = event.source();
|
||||
|
||||
let horizontal_amount = event
|
||||
.amount(Axis::Horizontal)
|
||||
.unwrap_or_else(|| event.amount_discrete(Axis::Horizontal).unwrap_or(0.0) * 3.0);
|
||||
|
||||
let vertical_amount = event
|
||||
.amount(Axis::Vertical)
|
||||
.unwrap_or_else(|| event.amount_discrete(Axis::Vertical).unwrap_or(0.0) * 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_msec()).source(source);
|
||||
|
||||
if horizontal_amount != 0.0 {
|
||||
frame = frame.value(Axis::Horizontal, horizontal_amount);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
frame = frame.discrete(Axis::Horizontal, discrete as i32);
|
||||
}
|
||||
} else if source == AxisSource::Finger {
|
||||
frame = frame.stop(Axis::Horizontal);
|
||||
}
|
||||
|
||||
if vertical_amount != 0.0 {
|
||||
frame = frame.value(Axis::Vertical, vertical_amount);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
frame = frame.discrete(Axis::Vertical, discrete as i32);
|
||||
}
|
||||
} else if source == AxisSource::Finger {
|
||||
frame = frame.stop(Axis::Vertical);
|
||||
}
|
||||
|
||||
self.seat.get_pointer().unwrap().axis(self, frame);
|
||||
}
|
||||
|
||||
fn keyboard<I: InputBackend>(&mut self, event: I::KeyboardKeyEvent) {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = event.time_msec();
|
||||
let press_state = event.state();
|
||||
let mut move_mode = false;
|
||||
let action = self.seat.get_keyboard().unwrap().input(
|
||||
self,
|
||||
event.key_code(),
|
||||
press_state,
|
||||
serial,
|
||||
time,
|
||||
|state, _modifiers, keysym| {
|
||||
if press_state == KeyState::Pressed {
|
||||
match keysym.modified_sym() {
|
||||
keysyms::KEY_L => return FilterResult::Intercept(1),
|
||||
keysyms::KEY_K => return FilterResult::Intercept(2),
|
||||
keysyms::KEY_J => return FilterResult::Intercept(3),
|
||||
keysyms::KEY_H => return FilterResult::Intercept(4),
|
||||
keysyms::KEY_Escape => {
|
||||
state.loop_signal.stop();
|
||||
return FilterResult::Intercept(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if keysym.modified_sym() == keysyms::KEY_Control_L {
|
||||
match press_state {
|
||||
KeyState::Pressed => {
|
||||
move_mode = true;
|
||||
}
|
||||
KeyState::Released => {
|
||||
move_mode = false;
|
||||
}
|
||||
}
|
||||
FilterResult::Forward
|
||||
} else {
|
||||
FilterResult::Forward
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
self.move_mode = move_mode;
|
||||
|
||||
let program = match action {
|
||||
Some(1) => "alacritty",
|
||||
Some(2) => "nautilus",
|
||||
Some(3) => "kitty",
|
||||
Some(4) => "foot",
|
||||
Some(_) | None => return,
|
||||
};
|
||||
|
||||
tracing::info!("Spawning {}", program);
|
||||
std::process::Command::new(program)
|
||||
.env("WAYLAND_DISPLAY", self.socket_name.clone())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl State<WinitData> {
|
||||
pub fn process_input_event<B: InputBackend>(
|
||||
&mut self,
|
||||
seat: &Seat<State<WinitData>>,
|
||||
event: InputEvent<B>,
|
||||
) {
|
||||
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||
match event {
|
||||
// TODO: rest of input events
|
||||
|
||||
// InputEvent::DeviceAdded { device } => todo!(),
|
||||
// InputEvent::DeviceRemoved { device } => todo!(),
|
||||
InputEvent::Keyboard { event } => {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let time = event.time_msec();
|
||||
let press_state = event.state();
|
||||
let mut move_mode = false;
|
||||
let action = seat.get_keyboard().unwrap().input(
|
||||
self,
|
||||
event.key_code(),
|
||||
press_state,
|
||||
serial,
|
||||
time,
|
||||
|_state, _modifiers, keysym| {
|
||||
if press_state == KeyState::Pressed {
|
||||
match keysym.modified_sym() {
|
||||
keysyms::KEY_L => return FilterResult::Intercept(1),
|
||||
keysyms::KEY_K => return FilterResult::Intercept(2),
|
||||
keysyms::KEY_J => return FilterResult::Intercept(3),
|
||||
keysyms::KEY_H => return FilterResult::Intercept(4),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if keysym.modified_sym() == keysyms::KEY_Control_L {
|
||||
match press_state {
|
||||
KeyState::Pressed => {
|
||||
move_mode = true;
|
||||
}
|
||||
KeyState::Released => {
|
||||
move_mode = false;
|
||||
}
|
||||
}
|
||||
FilterResult::Forward
|
||||
} else {
|
||||
FilterResult::Forward
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
self.move_mode = move_mode;
|
||||
|
||||
std::process::Command::new(match action {
|
||||
Some(1) => "alacritty",
|
||||
Some(2) => "nautilus",
|
||||
Some(3) => "kitty",
|
||||
Some(4) => "foot",
|
||||
Some(_) | None => return,
|
||||
})
|
||||
.env("WAYLAND_DISPLAY", self.socket_name.clone())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
}
|
||||
InputEvent::Keyboard { event } => self.keyboard::<B>(event),
|
||||
InputEvent::PointerMotion { event } => {}
|
||||
InputEvent::PointerMotionAbsolute { event } => {
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
let pointer_loc =
|
||||
event.position_transformed(output_geo.size) + output_geo.loc.to_f64();
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
self.pointer_location = pointer_loc;
|
||||
|
||||
let surface_under_pointer =
|
||||
self.space
|
||||
.element_under(pointer_loc)
|
||||
.and_then(|(window, location)| {
|
||||
window
|
||||
.surface_under(
|
||||
pointer_loc - location.to_f64(),
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.map(|(s, p)| (s, p + location))
|
||||
});
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
surface_under_pointer,
|
||||
&MotionEvent {
|
||||
location: pointer_loc,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
InputEvent::PointerButton { event } => {
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
let keyboard = seat.get_keyboard().unwrap();
|
||||
|
||||
// A serial is a number sent with a event that is sent back to the
|
||||
// server by the clients in further requests. This allows the server to
|
||||
// keep track of which event caused which requests. It is an AtomicU32
|
||||
// that increments when next_serial is called.
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
// Returns which button on the pointer was used.
|
||||
let button = event.button_code();
|
||||
|
||||
// The state, either released or pressed.
|
||||
let button_state = event.state();
|
||||
|
||||
let pointer_loc = pointer.current_location();
|
||||
|
||||
// If the button was clicked, focus on the window below if exists, else
|
||||
// unfocus on windows.
|
||||
if ButtonState::Pressed == button_state {
|
||||
if let Some((window, window_loc)) = self
|
||||
.space
|
||||
.element_under(pointer_loc)
|
||||
.map(|(w, l)| (w.clone(), l))
|
||||
{
|
||||
const BUTTON_LEFT: u32 = 0x110;
|
||||
const BUTTON_RIGHT: u32 = 0x111;
|
||||
if self.move_mode {
|
||||
if event.button_code() == BUTTON_LEFT {
|
||||
crate::xdg::request::move_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
seat,
|
||||
serial,
|
||||
);
|
||||
return; // TODO: kinda ugly return here
|
||||
} else if event.button_code() == BUTTON_RIGHT {
|
||||
let window_geometry = window.geometry();
|
||||
let window_x = window_loc.x as f64;
|
||||
let window_y = window_loc.y as f64;
|
||||
let window_width = window_geometry.size.w as f64;
|
||||
let window_height = window_geometry.size.h as f64;
|
||||
let half_width = window_x + window_width / 2.0;
|
||||
let half_height = window_y + window_height / 2.0;
|
||||
let full_width = window_x + window_width;
|
||||
let full_height = window_y + window_height;
|
||||
|
||||
println!(
|
||||
"window loc: {}, {} | window size: {}, {}",
|
||||
window_x, window_y, window_width, window_height
|
||||
);
|
||||
|
||||
let edges = match pointer_loc {
|
||||
Point { x, y, .. }
|
||||
if (window_x..=half_width).contains(&x)
|
||||
&& (window_y..=half_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::TopLeft
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (half_width..=full_width).contains(&x)
|
||||
&& (window_y..=half_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::TopRight
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (window_x..=half_width).contains(&x)
|
||||
&& (half_height..=full_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::BottomLeft
|
||||
}
|
||||
Point { x, y, .. }
|
||||
if (half_width..=full_width).contains(&x)
|
||||
&& (half_height..=full_height).contains(&y) =>
|
||||
{
|
||||
ResizeEdge::BottomRight
|
||||
}
|
||||
_ => ResizeEdge::None,
|
||||
};
|
||||
|
||||
crate::xdg::request::resize_request_force(
|
||||
self,
|
||||
window.toplevel(),
|
||||
seat,
|
||||
serial,
|
||||
edges,
|
||||
BUTTON_RIGHT,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Move window to top of stack.
|
||||
self.space.raise_element(&window, true);
|
||||
|
||||
// Focus on window.
|
||||
keyboard.set_focus(
|
||||
self,
|
||||
Some(window.toplevel().wl_surface().clone()),
|
||||
serial,
|
||||
);
|
||||
self.space.elements().for_each(|window| {
|
||||
window.toplevel().send_configure();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.space.elements().for_each(|window| {
|
||||
window.set_activated(false);
|
||||
window.toplevel().send_configure();
|
||||
});
|
||||
keyboard.set_focus(self, None, serial);
|
||||
}
|
||||
};
|
||||
|
||||
// Send the button event to the client.
|
||||
pointer.button(
|
||||
self,
|
||||
&ButtonEvent {
|
||||
button,
|
||||
state: button_state,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
InputEvent::PointerAxis { event } => {
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
let source = event.source();
|
||||
|
||||
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_msec()).source(source);
|
||||
|
||||
if horizontal_amount != 0.0 {
|
||||
frame = frame.value(Axis::Horizontal, horizontal_amount);
|
||||
if let Some(discrete) = horizontal_amount_discrete {
|
||||
frame = frame.discrete(Axis::Horizontal, discrete as i32);
|
||||
}
|
||||
} else if source == AxisSource::Finger {
|
||||
frame = frame.stop(Axis::Horizontal);
|
||||
}
|
||||
|
||||
if vertical_amount != 0.0 {
|
||||
frame = frame.value(Axis::Vertical, vertical_amount);
|
||||
if let Some(discrete) = vertical_amount_discrete {
|
||||
frame = frame.discrete(Axis::Vertical, discrete as i32);
|
||||
}
|
||||
} else if source == AxisSource::Finger {
|
||||
frame = frame.stop(Axis::Vertical);
|
||||
}
|
||||
|
||||
pointer.axis(self, frame);
|
||||
}
|
||||
InputEvent::PointerMotionAbsolute { event } => self.pointer_motion_absolute::<B>(event),
|
||||
InputEvent::PointerButton { event } => self.pointer_button::<B>(event),
|
||||
InputEvent::PointerAxis { event } => self.pointer_axis::<B>(event),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_motion_absolute<I: InputBackend>(&mut self, event: I::PointerMotionAbsoluteEvent) {
|
||||
let output = self.space.outputs().next().unwrap();
|
||||
let output_geo = self.space.output_geometry(output).unwrap();
|
||||
let pointer_loc = event.position_transformed(output_geo.size) + output_geo.loc.to_f64();
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
|
||||
// tracing::info!("pointer_loc: {:?}", pointer_loc);
|
||||
|
||||
self.pointer_location = pointer_loc;
|
||||
|
||||
let surface_under_pointer =
|
||||
self.space
|
||||
.element_under(pointer_loc)
|
||||
.and_then(|(window, location)| {
|
||||
window
|
||||
.surface_under(pointer_loc - location.to_f64(), WindowSurfaceType::ALL)
|
||||
.map(|(s, p)| (s, p + location))
|
||||
});
|
||||
|
||||
pointer.motion(
|
||||
self,
|
||||
surface_under_pointer,
|
||||
&MotionEvent {
|
||||
location: pointer_loc,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl State<UdevData> {
|
||||
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
|
||||
match event {
|
||||
// TODO: rest of input events
|
||||
|
||||
// InputEvent::DeviceAdded { device } => todo!(),
|
||||
// InputEvent::DeviceRemoved { device } => todo!(),
|
||||
InputEvent::Keyboard { event } => self.keyboard::<B>(event),
|
||||
InputEvent::PointerMotion { event } => self.pointer_motion::<B>(event),
|
||||
InputEvent::PointerMotionAbsolute { event } => self.pointer_motion_absolute::<B>(event),
|
||||
InputEvent::PointerButton { event } => self.pointer_button::<B>(event),
|
||||
InputEvent::PointerAxis { event } => self.pointer_axis::<B>(event),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_motion<I: InputBackend>(&mut self, event: I::PointerMotionEvent) {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
self.pointer_location += event.delta();
|
||||
|
||||
// clamp to screen limits
|
||||
// this event is never generated by winit
|
||||
self.pointer_location = self.clamp_coords(self.pointer_location);
|
||||
|
||||
// tracing::info!("{:?}", self.pointer_location);
|
||||
if let Some(ptr) = self.seat.get_pointer() {
|
||||
ptr.motion(
|
||||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: self.pointer_location,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
|
||||
// ptr.relative_motion(
|
||||
// self,
|
||||
// under,
|
||||
// &RelativeMotionEvent {
|
||||
// delta: event.delta(),
|
||||
// delta_unaccel: event.delta_unaccel(),
|
||||
// utime: event.time(),
|
||||
// },
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_motion_absolute<I: InputBackend>(&mut self, event: I::PointerMotionAbsoluteEvent) {
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
|
||||
let max_x = self.space.outputs().fold(0, |acc, o| {
|
||||
acc + self.space.output_geometry(o).unwrap().size.w
|
||||
});
|
||||
|
||||
let max_h_output = self
|
||||
.space
|
||||
.outputs()
|
||||
.max_by_key(|o| self.space.output_geometry(o).unwrap().size.h)
|
||||
.unwrap();
|
||||
|
||||
let max_y = self.space.output_geometry(max_h_output).unwrap().size.h;
|
||||
|
||||
self.pointer_location.x = event.x_transformed(max_x);
|
||||
self.pointer_location.y = event.y_transformed(max_y);
|
||||
|
||||
// clamp to screen limits
|
||||
self.pointer_location = self.clamp_coords(self.pointer_location);
|
||||
|
||||
// tracing::info!("{:?}", self.pointer_location);
|
||||
|
||||
// let under = self.surface_under();
|
||||
if let Some(ptr) = self.seat.get_pointer() {
|
||||
ptr.motion(
|
||||
self,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: self.pointer_location,
|
||||
serial,
|
||||
time: event.time_msec(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_coords(&self, pos: Point<f64, Logical>) -> Point<f64, Logical> {
|
||||
if self.space.outputs().next().is_none() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
let (pos_x, pos_y) = pos.into();
|
||||
let max_x = self.space.outputs().fold(0, |acc, o| {
|
||||
acc + self.space.output_geometry(o).unwrap().size.w
|
||||
});
|
||||
let clamped_x = pos_x.clamp(0.0, max_x as f64);
|
||||
let max_y = self
|
||||
.space
|
||||
.outputs()
|
||||
.find(|o| {
|
||||
let geo = self.space.output_geometry(o).unwrap();
|
||||
geo.contains((clamped_x as i32, 0))
|
||||
})
|
||||
.map(|o| self.space.output_geometry(o).unwrap().size.h);
|
||||
|
||||
if let Some(max_y) = max_y {
|
||||
let clamped_y = pos_y.clamp(0.0, max_y as f64);
|
||||
(clamped_x, clamped_y).into()
|
||||
} else {
|
||||
(clamped_x, pos_y).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
mod backend;
|
||||
mod cursor;
|
||||
mod grab;
|
||||
mod handlers;
|
||||
mod input;
|
||||
|
@ -25,6 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
|
||||
tracing::info!("Starting winit backend");
|
||||
crate::backend::winit::run_winit()?;
|
||||
// crate::backend::winit::run_winit()?;
|
||||
crate::backend::udev::run_udev()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
136
src/state.rs
136
src/state.rs
|
@ -1,10 +1,18 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{error::Error, os::fd::AsRawFd, sync::Arc};
|
||||
|
||||
use smithay::{
|
||||
desktop::{PopupManager, Space, Window},
|
||||
input::{pointer::CursorImageStatus, SeatState},
|
||||
backend::renderer::element::RenderElementStates,
|
||||
desktop::{
|
||||
utils::{
|
||||
surface_presentation_feedback_flags_from_states, surface_primary_scanout_output,
|
||||
OutputPresentationFeedback,
|
||||
},
|
||||
PopupManager, Space, Window,
|
||||
},
|
||||
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
|
||||
output::Output,
|
||||
reexports::{
|
||||
calloop::{LoopHandle, LoopSignal},
|
||||
calloop::{generic::Generic, Interest, LoopHandle, LoopSignal, Mode, PostAction},
|
||||
wayland_server::{
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
protocol::wl_surface::WlSurface,
|
||||
|
@ -15,27 +23,31 @@ use smithay::{
|
|||
wayland::{
|
||||
compositor::{CompositorClientState, CompositorState},
|
||||
data_device::DataDeviceState,
|
||||
dmabuf::DmabufFeedback,
|
||||
fractional_scale::FractionalScaleManagerState,
|
||||
output::OutputManagerState,
|
||||
seat::WaylandFocus,
|
||||
shell::xdg::XdgShellState,
|
||||
shm::ShmState,
|
||||
socket::ListeningSocketSource,
|
||||
viewporter::ViewporterState,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::backend::{winit::WinitData, Backend};
|
||||
use crate::backend::Backend;
|
||||
|
||||
pub struct State<B: Backend> {
|
||||
pub backend_data: B,
|
||||
|
||||
pub loop_signal: LoopSignal,
|
||||
pub loop_handle: LoopHandle<'static, CalloopData>,
|
||||
pub loop_handle: LoopHandle<'static, CalloopData<B>>,
|
||||
pub clock: Clock<Monotonic>,
|
||||
|
||||
pub space: Space<Window>,
|
||||
pub move_mode: bool,
|
||||
pub socket_name: OsString,
|
||||
pub socket_name: String,
|
||||
|
||||
pub seat: Seat<State<B>>,
|
||||
|
||||
pub compositor_state: CompositorState,
|
||||
pub data_device_state: DataDeviceState,
|
||||
|
@ -53,6 +65,72 @@ pub struct State<B: Backend> {
|
|||
}
|
||||
|
||||
impl<B: Backend> State<B> {
|
||||
pub fn init(
|
||||
backend_data: B,
|
||||
display: &mut Display<State<B>>,
|
||||
loop_signal: LoopSignal,
|
||||
loop_handle: LoopHandle<'static, CalloopData<B>>,
|
||||
) -> Result<Self, Box<dyn Error>> {
|
||||
let socket = ListeningSocketSource::new_auto()?;
|
||||
let socket_name = socket.socket_name().to_os_string();
|
||||
|
||||
std::env::set_var("WAYLAND_DISPLAY", socket_name.clone());
|
||||
|
||||
loop_handle.insert_source(socket, |stream, _metadata, data| {
|
||||
data.display
|
||||
.handle()
|
||||
.insert_client(stream, Arc::new(ClientState::default()))
|
||||
.unwrap();
|
||||
})?;
|
||||
|
||||
loop_handle.insert_source(
|
||||
Generic::new(
|
||||
display.backend().poll_fd().as_raw_fd(),
|
||||
Interest::READ,
|
||||
Mode::Level,
|
||||
),
|
||||
|_readiness, _metadata, data| {
|
||||
data.display.dispatch_clients(&mut data.state)?;
|
||||
Ok(PostAction::Continue)
|
||||
},
|
||||
)?;
|
||||
|
||||
let display_handle = display.handle();
|
||||
let mut seat_state = SeatState::new();
|
||||
let mut seat = seat_state.new_wl_seat(&display_handle, backend_data.seat_name());
|
||||
seat.add_pointer();
|
||||
seat.add_keyboard(XkbConfig::default(), 200, 25)?;
|
||||
|
||||
Ok(Self {
|
||||
backend_data,
|
||||
loop_signal,
|
||||
loop_handle,
|
||||
clock: Clock::<Monotonic>::new()?,
|
||||
compositor_state: CompositorState::new::<State<B>>(&display_handle),
|
||||
data_device_state: DataDeviceState::new::<State<B>>(&display_handle),
|
||||
seat_state,
|
||||
pointer_location: (0.0, 0.0).into(),
|
||||
shm_state: ShmState::new::<State<B>>(&display_handle, vec![]),
|
||||
space: Space::<Window>::default(),
|
||||
cursor_status: CursorImageStatus::Default,
|
||||
output_manager_state: OutputManagerState::new_with_xdg_output::<State<B>>(
|
||||
&display_handle,
|
||||
),
|
||||
xdg_shell_state: XdgShellState::new::<State<B>>(&display_handle),
|
||||
viewporter_state: ViewporterState::new::<State<B>>(&display_handle),
|
||||
fractional_scale_manager_state: FractionalScaleManagerState::new::<State<B>>(
|
||||
&display_handle,
|
||||
),
|
||||
|
||||
seat,
|
||||
|
||||
move_mode: false,
|
||||
socket_name: socket_name.to_string_lossy().to_string(),
|
||||
|
||||
popup_manager: PopupManager::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [Window] associated with a given [WlSurface].
|
||||
pub fn window_for_surface(&self, surface: &WlSurface) -> Option<Window> {
|
||||
self.space
|
||||
|
@ -62,9 +140,9 @@ impl<B: Backend> State<B> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct CalloopData {
|
||||
pub display: Display<State<WinitData>>,
|
||||
pub state: State<WinitData>,
|
||||
pub struct CalloopData<B: Backend> {
|
||||
pub display: Display<State<B>>,
|
||||
pub state: State<B>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -78,3 +156,41 @@ impl ClientData for ClientState {
|
|||
|
||||
// fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct SurfaceDmabufFeedback<'a> {
|
||||
pub render_feedback: &'a DmabufFeedback,
|
||||
pub scanout_feedback: &'a DmabufFeedback,
|
||||
}
|
||||
|
||||
pub fn take_presentation_feedback(
|
||||
output: &Output,
|
||||
space: &Space<Window>,
|
||||
render_element_states: &RenderElementStates,
|
||||
) -> OutputPresentationFeedback {
|
||||
let mut output_presentation_feedback = OutputPresentationFeedback::new(output);
|
||||
|
||||
space.elements().for_each(|window| {
|
||||
if space.outputs_for_element(window).contains(output) {
|
||||
window.take_presentation_feedback(
|
||||
&mut output_presentation_feedback,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
surface_presentation_feedback_flags_from_states(surface, render_element_states)
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
let map = smithay::desktop::layer_map_for_output(output);
|
||||
for layer_surface in map.layers() {
|
||||
layer_surface.take_presentation_feedback(
|
||||
&mut output_presentation_feedback,
|
||||
surface_primary_scanout_output,
|
||||
|surface, _| {
|
||||
surface_presentation_feedback_flags_from_states(surface, render_element_states)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
output_presentation_feedback
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue