Copied Anvil's udev support

This commit is contained in:
Seaotatop 2023-06-09 20:29:17 -05:00
parent 65760b2434
commit 748dadae01
12 changed files with 2308 additions and 557 deletions

View file

@ -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

Binary file not shown.

BIN
resources/numbers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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
View 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)
}

View file

@ -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>);

View file

@ -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()
}
}
}

View file

@ -0,0 +1 @@

View file

@ -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(())
}

View file

@ -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
}