First commit

This commit is contained in:
Seaotatop 2023-05-28 19:14:30 -05:00
parent 1c1c685385
commit b5d24956db
12 changed files with 1310 additions and 0 deletions

10
Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "pinnacle"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tracing = "0.1.37"
smithay = { git = "https://github.com/Smithay/smithay" }

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
error_on_line_overflow = true

2
src/grab.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod move_grab;
pub mod resize_grab;

82
src/grab/move_grab.rs Normal file
View file

@ -0,0 +1,82 @@
use smithay::{
desktop::Window,
// NOTE: maybe alias this to PointerGrabStartData because there's another GrabStartData in
// | input::keyboard
input::{
pointer::PointerGrab,
pointer::{
AxisFrame, ButtonEvent, GrabStartData, MotionEvent, PointerInnerHandle,
RelativeMotionEvent,
},
SeatHandler,
},
utils::{IsAlive, Logical, Point},
};
use crate::State;
pub struct MoveSurfaceGrab<S: SeatHandler> {
pub start_data: GrabStartData<S>,
pub window: Window,
pub initial_window_loc: Point<i32, Logical>,
}
impl PointerGrab<State> for MoveSurfaceGrab<State> {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(<State as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &MotionEvent,
) {
handle.motion(data, None, event);
if !self.window.alive() {
handle.unset_grab(data, event.serial, event.time);
return;
}
let delta = event.location - self.start_data.location;
let new_loc = self.initial_window_loc.to_f64() + delta;
data.space
.map_element(self.window.clone(), new_loc.to_i32_round(), true);
}
fn relative_motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(<State as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &ButtonEvent,
) {
handle.button(data, event);
const BUTTON_LEFT: u32 = 0x110;
if !handle.current_pressed().contains(&BUTTON_LEFT) {
handle.unset_grab(data, event.serial, event.time);
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details);
}
fn start_data(&self) -> &GrabStartData<State> {
&self.start_data
}
}

256
src/grab/resize_grab.rs Normal file
View file

@ -0,0 +1,256 @@
use smithay::{
desktop::{Space, Window},
input::{
pointer::{AxisFrame, ButtonEvent, GrabStartData, PointerGrab, PointerInnerHandle},
SeatHandler,
},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
wayland_server::protocol::wl_surface::WlSurface,
},
utils::{IsAlive, Logical, Point, Rectangle, Size},
wayland::shell::xdg::SurfaceCachedState,
};
use crate::{window::SurfaceState, State};
pub struct ResizeSurfaceGrab<S: SeatHandler> {
start_data: GrabStartData<S>,
window: Window,
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
last_window_size: Size<i32, Logical>,
button_used: u32,
}
impl<S: SeatHandler> ResizeSurfaceGrab<S> {
pub fn start(
start_data: GrabStartData<S>,
window: Window,
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
button_used: u32,
) -> Self {
ResizeSurfaceState::with_state(window.toplevel().wl_surface(), |state| {
*state = ResizeSurfaceState::Resizing {
edges,
initial_window_rect,
};
});
Self {
start_data,
window,
edges,
initial_window_rect,
last_window_size: initial_window_rect.size,
button_used,
}
}
}
impl PointerGrab<State> for ResizeSurfaceGrab<State> {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(<State as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &smithay::input::pointer::MotionEvent,
) {
handle.motion(data, None, event);
if !self.window.alive() {
handle.unset_grab(data, event.serial, event.time);
return;
}
let delta = (event.location - self.start_data.location).to_i32_round::<i32>();
let mut new_window_width = self.initial_window_rect.size.w;
let mut new_window_height = self.initial_window_rect.size.h;
if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = self.edges {
new_window_width = self.initial_window_rect.size.w - delta.x;
}
if let ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight = self.edges {
new_window_width = self.initial_window_rect.size.w + delta.x;
}
if let ResizeEdge::Top | ResizeEdge::TopRight | ResizeEdge::TopLeft = self.edges {
new_window_height = self.initial_window_rect.size.h - delta.y;
}
if let ResizeEdge::Bottom | ResizeEdge::BottomRight | ResizeEdge::BottomLeft = self.edges {
new_window_height = self.initial_window_rect.size.h + delta.y;
}
let (min_size, max_size) = smithay::wayland::compositor::with_states(
self.window.toplevel().wl_surface(),
|states| {
let data = states.cached_state.current::<SurfaceCachedState>();
(data.min_size, data.max_size)
},
);
let min_width = i32::min(1, min_size.w);
let min_height = i32::min(1, min_size.h);
let max_width = if max_size.w != 0 {
max_size.w
} else {
i32::MAX
};
let max_height = if max_size.h != 0 {
max_size.h
} else {
i32::MAX
};
self.last_window_size = Size::from((
new_window_width.clamp(min_width, max_width),
new_window_height.clamp(min_height, max_height),
));
let toplevel_surface = self.window.toplevel();
toplevel_surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
toplevel_surface.send_pending_configure();
}
fn relative_motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(<State as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &smithay::input::pointer::RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &ButtonEvent,
) {
handle.button(data, event);
if !handle.current_pressed().contains(&self.button_used) {
handle.unset_grab(data, event.serial, event.time);
let toplevel_surface = self.window.toplevel();
toplevel_surface.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
toplevel_surface.send_pending_configure();
ResizeSurfaceState::with_state(toplevel_surface.wl_surface(), |state| {
*state = ResizeSurfaceState::WaitingForLastCommit {
edges: self.edges,
initial_window_rect: self.initial_window_rect,
};
});
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details);
}
fn start_data(&self) -> &GrabStartData<State> {
&self.start_data
}
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
enum ResizeSurfaceState {
#[default]
Idle,
Resizing {
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
},
WaitingForLastCommit {
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
},
}
impl ResizeSurfaceState {
fn commit(&mut self) -> Option<(ResizeEdge, Rectangle<i32, Logical>)> {
match *self {
Self::Idle => None,
Self::Resizing {
edges,
initial_window_rect,
} => Some((edges, initial_window_rect)),
Self::WaitingForLastCommit {
edges,
initial_window_rect,
} => {
*self = Self::Idle;
Some((edges, initial_window_rect))
}
}
}
}
impl SurfaceState for ResizeSurfaceState {}
pub fn handle_commit(space: &mut Space<Window>, surface: &WlSurface) -> Option<()> {
let window = space
.elements()
.find(|w| w.toplevel().wl_surface() == surface)
.cloned()?;
let mut window_loc = space.element_location(&window)?;
let geometry = window.geometry();
let new_loc: Point<Option<i32>, Logical> = ResizeSurfaceState::with_state(surface, |state| {
state
.commit()
.map(|(edges, initial_window_rect)| {
let mut new_x: Option<i32> = None;
let mut new_y: Option<i32> = None;
if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = edges {
new_x = Some(
initial_window_rect.loc.x + (initial_window_rect.size.w - geometry.size.w),
);
}
if let ResizeEdge::Top | ResizeEdge::TopLeft | ResizeEdge::TopRight = edges {
new_y = Some(
initial_window_rect.loc.y + (initial_window_rect.size.h - geometry.size.h),
);
}
(new_x, new_y)
})
.unwrap_or_default()
.into()
});
if let Some(new_x) = new_loc.x {
window_loc.x = new_x;
}
if let Some(new_y) = new_loc.y {
window_loc.y = new_y;
}
if new_loc.x.is_some() || new_loc.y.is_some() {
space.map_element(window, window_loc, false);
}
Some(())
}

169
src/handlers.rs Normal file
View file

@ -0,0 +1,169 @@
use smithay::{
backend::renderer::utils,
delegate_compositor, delegate_data_device, delegate_output, delegate_seat, delegate_shm,
delegate_xdg_shell,
desktop::Window,
input::{pointer::CursorImageStatus, Seat, SeatHandler, SeatState},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
wayland_server::{
protocol::{wl_buffer::WlBuffer, wl_seat::WlSeat, wl_surface::WlSurface},
Client,
},
},
wayland::{
buffer::BufferHandler,
compositor::{self, CompositorClientState, CompositorHandler, CompositorState},
data_device::{
ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler,
},
shell::xdg::{
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
XdgToplevelSurfaceData,
},
shm::{ShmHandler, ShmState},
},
};
use crate::{ClientState, State};
impl BufferHandler for State {
fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
}
impl CompositorHandler for State {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn commit(&mut self, surface: &WlSurface) {
utils::on_commit_buffer_handler::<Self>(surface);
if let Some(window) = self
.space
.elements()
.find(|w| w.toplevel().wl_surface() == surface)
.cloned()
{
// TODO: from smallvil: check if subsurfaces are synced then do on_commit or something
window.on_commit();
let initial_configure_sent = compositor::with_states(surface, |states| {
states
.data_map
.get::<XdgToplevelSurfaceData>()
.unwrap()
.lock()
.unwrap()
.initial_configure_sent
});
if !initial_configure_sent {
window.toplevel().send_configure();
}
}
crate::grab::resize_grab::handle_commit(&mut self.space, surface);
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&client.get_data::<ClientState>().unwrap().compositor_state
}
}
delegate_compositor!(State);
impl ClientDndGrabHandler for State {}
impl ServerDndGrabHandler for State {}
impl DataDeviceHandler for State {
fn data_device_state(&self) -> &DataDeviceState {
&self.data_device_state
}
}
delegate_data_device!(State);
impl SeatHandler for State {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
}
fn cursor_image(&mut self, _seat: &smithay::input::Seat<Self>, _image: CursorImageStatus) {
self.cursor_status = _image;
}
fn focus_changed(
&mut self,
_seat: &smithay::input::Seat<Self>,
_focused: Option<&Self::KeyboardFocus>,
) {
}
}
delegate_seat!(State);
impl ShmHandler for State {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
delegate_shm!(State);
impl XdgShellHandler for State {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = Window::new(surface);
self.space.map_element(window, (50, 50), true);
// TODO: refresh all window geometries
}
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
// TODO: refresh geometries
}
fn new_popup(&mut self, surface: PopupSurface, positioner: PositionerState) {}
fn move_request(
&mut self,
surface: ToplevelSurface,
seat: WlSeat,
serial: smithay::utils::Serial,
) {
crate::xdg::request::move_request(
self,
&surface,
&Seat::from_resource(&seat).unwrap(),
serial,
);
}
fn resize_request(
&mut self,
surface: ToplevelSurface,
seat: WlSeat,
serial: smithay::utils::Serial,
edges: ResizeEdge,
) {
const BUTTON_LEFT: u32 = 0x110;
crate::xdg::request::resize_request(
self,
&surface,
&Seat::from_resource(&seat).unwrap(),
serial,
edges,
BUTTON_LEFT,
);
}
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: smithay::utils::Serial) {}
// TODO: impl the rest of the fns in XdgShellHandler
}
delegate_xdg_shell!(State);
delegate_output!(State);

492
src/main.rs Normal file
View file

@ -0,0 +1,492 @@
mod grab;
mod handlers;
mod pointer;
mod window;
mod xdg;
use std::{error::Error, os::fd::AsRawFd, sync::Arc, time::Duration};
use smithay::{
backend::{
input::{
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputEvent, KeyState,
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
},
renderer::{
damage::OutputDamageTracker, element::surface::WaylandSurfaceRenderElement,
gles::GlesRenderer,
},
winit::{WinitError, WinitEvent},
},
desktop::{space, Space, Window, WindowSurfaceType},
input::{
keyboard::{keysyms, FilterResult},
pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent},
SeatState,
},
output::{Output, Subpixel},
reexports::{
calloop::{
generic::Generic,
timer::{TimeoutAction, Timer},
EventLoop, Interest, LoopHandle, LoopSignal, Mode, PostAction,
},
wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
wayland_server::{
backend::{ClientData, ClientId, DisconnectReason},
Display,
},
},
utils::{Clock, Logical, Monotonic, Physical, Point, Scale, Transform, SERIAL_COUNTER},
wayland::{
compositor::{CompositorClientState, CompositorState},
data_device::DataDeviceState,
output::OutputManagerState,
shell::xdg::XdgShellState,
shm::ShmState,
socket::ListeningSocketSource,
},
};
fn main() -> Result<(), Box<dyn Error>> {
let mut event_loop: EventLoop<Data> = EventLoop::try_new()?;
let mut display: Display<State> = 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>::new();
let mut seat = seat_state.new_wl_seat(&display_handle, "seat1");
seat.add_keyboard(Default::default(), 500, 50)?;
seat.add_pointer();
let state = State {
loop_signal: event_loop.get_signal(),
loop_handle: event_loop.handle(),
clock: Clock::<Monotonic>::new()?,
compositor_state: CompositorState::new::<State>(&display_handle),
data_device_state: DataDeviceState::new::<State>(&display_handle),
seat_state,
shm_state: ShmState::new::<State>(&display_handle, Vec::new()),
space: Space::<Window>::default(),
cursor_status: CursorImageStatus::Default,
pointer_location: (0.0, 0.0).into(),
output_manager_state: OutputManagerState::new_with_xdg_output::<State>(&display_handle),
xdg_shell_state: XdgShellState::new::<State>(&display_handle),
move_mode: false,
};
let mut data = Data { display, state };
let (mut winit_backend, mut winit_evt_loop) = smithay::backend::winit::init::<GlesRenderer>()?;
let mode = smithay::output::Mode {
size: winit_backend.window_size().physical_size,
refresh: 60_000,
};
let physical_properties = smithay::output::PhysicalProperties {
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "Comp make".to_string(),
model: "Comp model".to_string(),
};
let output = Output::new("27GL83A".to_string(), physical_properties);
output.create_global::<State>(&display_handle);
output.change_current_state(
Some(mode),
Some(Transform::Flipped180),
None,
Some((0, 0).into()),
);
output.set_preferred(mode);
data.state.space.map_output(&output, (0, 0));
std::env::set_var("WAYLAND_DISPLAY", socket_name);
let start_time = std::time::Instant::now();
let timer = Timer::immediate();
let mut damage_tracker = OutputDamageTracker::from_output(&output);
// TODO: pointer
evt_loop_handle.insert_source(timer, 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: 60_000,
}),
None,
None,
None,
);
}
WinitEvent::Focus(_) => {}
WinitEvent::Input(input_evt) => match input_evt {
// TODO: extract input events
// | into separate function
// 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(
state,
event.key_code(),
press_state,
serial,
time,
|_a, _modifiers, keysym| {
if press_state == KeyState::Pressed
&& keysym.modified_sym() == keysyms::KEY_L
{
FilterResult::Intercept(1)
} else 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
}
},
);
state.move_mode = move_mode;
if action == Some(1) {
std::process::Command::new("alacritty").spawn().unwrap();
}
}
InputEvent::PointerMotion { event } => {}
InputEvent::PointerMotionAbsolute { event } => {
let output = state.space.outputs().next().unwrap();
let output_geo = state.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();
let surface_under_pointer = state
.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(
state,
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)) = state
.space
.element_under(pointer_loc)
.map(|(w, l)| (w.clone(), l))
{
const BUTTON_LEFT: u32 = 0x110;
const BUTTON_RIGHT: u32 = 0x111;
if state.move_mode {
if event.button_code() == BUTTON_LEFT {
// BTN_RIGHT
crate::xdg::request::move_request_force(
state,
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(
state,
window.toplevel(),
&seat,
serial,
edges,
BUTTON_RIGHT,
);
}
} else {
// Move window to top of stack.
state.space.raise_element(&window, true);
// Focus on window.
keyboard.set_focus(
state,
Some(window.toplevel().wl_surface().clone()),
serial,
);
state.space.elements().for_each(|window| {
window.toplevel().send_configure();
});
}
} else {
state.space.elements().for_each(|window| {
window.set_activated(false);
window.toplevel().send_configure();
});
keyboard.set_focus(state, None, serial);
}
};
// Send the button event to the client.
pointer.button(
state,
&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);
}
println!("axisframe: {:?}", frame);
pointer.axis(state, frame);
}
// TODO: rest of the InputEvents
_ => (),
},
WinitEvent::Refresh => {}
}
});
match result {
Ok(_) => {}
Err(WinitError::WindowClosed) => {
state.loop_signal.stop();
}
};
winit_backend.bind().unwrap();
let scale = Scale::from(output.current_scale().fractional_scale());
let cursor_pos = state.pointer_location;
let _cursor_pos_scaled: Point<i32, Physical> = cursor_pos.to_physical(scale).to_i32_round();
space::render_output::<_, WaylandSurfaceRenderElement<GlesRenderer>, _, _>(
&output,
winit_backend.renderer(),
1.0,
0,
[&state.space],
&[],
&mut damage_tracker,
[0.1, 0.1, 0.1, 1.0],
)
.unwrap();
winit_backend.submit(None).unwrap();
state.space.elements().for_each(|window| {
window.send_frame(
&output,
start_time.elapsed(),
Some(Duration::ZERO),
|_, _| Some(output.clone()),
)
});
state.space.refresh();
display.flush_clients().unwrap();
TimeoutAction::ToDuration(Duration::from_millis(16))
})?;
event_loop.run(None, &mut data, |_data| {})?;
Ok(())
}
pub struct State {
pub loop_signal: LoopSignal,
pub loop_handle: LoopHandle<'static, Data>,
pub clock: Clock<Monotonic>,
pub compositor_state: CompositorState,
pub data_device_state: DataDeviceState,
pub seat_state: SeatState<Self>,
pub shm_state: ShmState,
pub space: Space<Window>,
pub cursor_status: CursorImageStatus,
pub pointer_location: Point<f64, Logical>,
pub output_manager_state: OutputManagerState,
pub xdg_shell_state: XdgShellState,
pub move_mode: bool,
}
pub struct Data {
pub display: Display<State>,
pub state: State,
}
#[derive(Default)]
struct ClientState {
pub compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
// fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {}
}

35
src/pointer.rs Normal file
View file

@ -0,0 +1,35 @@
use smithay::{
input::{
pointer::{GrabStartData, PointerHandle},
SeatHandler,
},
reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource},
utils::Serial,
};
/// Returns the [GrabStartData] from a pointer grab, if any.
pub fn pointer_grab_start_data<S>(
pointer: &PointerHandle<S>,
surface: &WlSurface,
serial: Serial,
) -> Option<GrabStartData<S>>
where
S: SeatHandler<PointerFocus = WlSurface> + 'static,
{
println!("start of pointer_grab_start_data");
if !pointer.has_grab(serial) {
println!("pointer doesn't have grab");
return None;
}
let start_data = pointer.grab_start_data()?;
let (focus_surface, _point) = start_data.focus.as_ref()?;
if !focus_surface.id().same_client_as(&surface.id()) {
println!("surface isn't the same");
return None;
}
Some(start_data)
}

58
src/window.rs Normal file
View file

@ -0,0 +1,58 @@
use std::cell::RefCell;
use smithay::{reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor};
use crate::State;
use self::window_state::{Float, WindowState};
pub mod window_state;
pub trait SurfaceState: Default + 'static {
fn with_state<F, T>(wl_surface: &WlSurface, function: F) -> T
where
F: FnOnce(&mut Self) -> T,
{
compositor::with_states(wl_surface, |states| {
states.data_map.insert_if_missing(RefCell::<Self>::default);
let state = states.data_map.get::<RefCell<Self>>().unwrap();
function(&mut state.borrow_mut())
})
}
}
pub fn toggle_floating(state: &mut State, wl_surface: &WlSurface) {
WindowState::with_state(wl_surface, |window_state| {
let window = state
.space
.elements()
.find(|w| w.toplevel().wl_surface() == wl_surface)
.unwrap()
.clone();
match window_state.floating {
Float::NotFloating(prev_loc_and_size) => {
if let Some((prev_loc, prev_size)) = prev_loc_and_size {
window.toplevel().with_pending_state(|state| {
state.size = Some(prev_size);
});
window.toplevel().send_pending_configure();
state.space.map_element(window, prev_loc, false); // TODO: should it activate?
}
window_state.floating = Float::Floating
}
Float::Floating => {
// TODO: recompute all non-floating window positions
window_state.floating = Float::NotFloating(Some((
state.space.element_location(&window).unwrap(), // We get the location this way
// because window.geometry().loc doesn't seem to be the actual location
window.geometry().size,
)));
}
}
})
}

View file

@ -0,0 +1,30 @@
use smithay::utils::{Logical, Point, Size};
use super::SurfaceState;
pub struct WindowState {
pub floating: Float,
}
pub enum Float {
NotFloating(Option<(Point<i32, Logical>, Size<i32, Logical>)>),
/// An [Option] of a tuple of the previous location and previous size of the window
Floating,
}
impl Default for WindowState {
fn default() -> Self {
Self::new() // TODO: maybe actual defaults
}
}
impl WindowState {
pub fn new() -> Self {
Self {
floating: Float::NotFloating(None), // TODO: get this from a config file instead of
// | hardcoding
}
}
}
impl SurfaceState for WindowState {}

1
src/xdg.rs Normal file
View file

@ -0,0 +1 @@
pub mod request;

174
src/xdg/request.rs Normal file
View file

@ -0,0 +1,174 @@
use smithay::{
input::{pointer::Focus, Seat},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::Rectangle,
wayland::shell::xdg::ToplevelSurface,
};
use crate::{
grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab},
State,
};
pub fn move_request(
state: &mut State,
surface: &ToplevelSurface,
seat: &Seat<State>,
serial: smithay::utils::Serial,
) {
println!("move_request started");
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap();
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial)
{
let window = state
.space
.elements()
.find(|w| w.toplevel().wl_surface() == wl_surface)
.unwrap()
.clone();
let initial_window_loc = state.space.element_location(&window).unwrap();
let grab = MoveSurfaceGrab {
start_data,
window,
initial_window_loc,
};
pointer.set_grab(state, grab, serial, Focus::Clear);
} else {
println!("no grab start data");
}
}
// TODO: see how this interacts with drag and drop and other grabs
pub fn move_request_force(
state: &mut State,
surface: &ToplevelSurface,
seat: &Seat<State>,
serial: smithay::utils::Serial,
) {
println!("move_request_force started");
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap();
let window = state
.space
.elements()
.find(|w| w.toplevel().wl_surface() == wl_surface)
.unwrap()
.clone();
let initial_window_loc = state.space.element_location(&window).unwrap();
let start_data = smithay::input::pointer::GrabStartData {
focus: pointer
.current_focus()
.map(|focus| (focus, initial_window_loc)),
button: 0x110,
location: pointer.current_location(),
};
let grab = MoveSurfaceGrab {
start_data,
window,
initial_window_loc,
};
pointer.set_grab(state, grab, serial, Focus::Clear);
}
pub fn resize_request(
state: &mut State,
surface: &ToplevelSurface,
seat: &Seat<State>,
serial: smithay::utils::Serial,
edges: xdg_toplevel::ResizeEdge,
button_used: u32,
) {
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap();
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial)
{
let window = state
.space
.elements()
.find(|w| w.toplevel().wl_surface() == wl_surface)
.unwrap()
.clone(); // TODO: move this search into its own function
let initial_window_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size;
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing);
});
surface.send_pending_configure();
let grab = ResizeSurfaceGrab::start(
start_data,
window,
edges,
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
button_used,
);
pointer.set_grab(state, grab, serial, Focus::Clear);
}
}
pub fn resize_request_force(
state: &mut State,
surface: &ToplevelSurface,
seat: &Seat<State>,
serial: smithay::utils::Serial,
edges: xdg_toplevel::ResizeEdge,
button_used: u32,
) {
println!("resize_request_force started with edges {:?}", edges);
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap();
let window = state
.space
.elements()
.find(|w| w.toplevel().wl_surface() == wl_surface)
.unwrap()
.clone(); // TODO: move this search into its own function
let initial_window_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size;
surface.with_pending_state(|state| {
println!("setting xdg state to Resizing");
state.states.set(xdg_toplevel::State::Resizing);
});
surface.send_pending_configure();
let start_data = smithay::input::pointer::GrabStartData {
focus: pointer
.current_focus()
.map(|focus| (focus, initial_window_loc)),
button: button_used,
location: pointer.current_location(),
};
let grab = ResizeSurfaceGrab::start(
start_data,
window,
edges,
Rectangle::from_loc_and_size(initial_window_loc, initial_window_size),
button_used,
);
pointer.set_grab(state, grab, serial, Focus::Clear);
}