This commit is contained in:
IoIxD 2026-07-30 14:01:16 -07:00
commit 0667b031cc
12 changed files with 723 additions and 522 deletions

View file

@ -1,5 +0,0 @@
pub mod move_grab;
pub use move_grab::MoveSurfaceGrab;
pub mod resize_grab;
pub use resize_grab::ResizeSurfaceGrab;

View file

@ -1,160 +0,0 @@
//! Move grab is the state of a composer during which the client window is being dragged around.
//!
//! eg. Usually whenever a user clicks on the app's titlebar and starts dragging, the compositors
//! enters a MoveSurfaceGrab state.
use crate::{render::DecoratedWindow, State};
use smithay::{
desktop::Window,
input::pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,
MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,
},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::{Logical, Point},
};
pub struct MoveSurfaceGrab {
pub start_data: PointerGrabStartData<State>,
pub window: DecoratedWindow,
pub initial_window_location: Point<i32, Logical>,
}
impl PointerGrab<State> for MoveSurfaceGrab {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(WlSurface, Point<f64, Logical>)>,
event: &MotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
let delta = event.location - self.start_data.location;
let new_location = self.initial_window_location.to_f64() + delta;
data.space
.map_element(self.window.clone(), new_location.to_i32_round(), true);
}
fn relative_motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(WlSurface, Point<f64, 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);
// The button is a button code as defined in the
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
const BTN_LEFT: u32 = 0x110;
if !handle.current_pressed().contains(&BTN_LEFT) {
// No more buttons are pressed, release the grab.
handle.unset_grab(self, data, event.serial, event.time, true);
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<State> {
&self.start_data
}
fn unset(&mut self, _data: &mut State) {}
}

View file

@ -1,7 +1,8 @@
use smithay::{
desktop::{layer_map_for_output, LayerSurface},
output::Output,
wayland::shell::wlr_layer::WlrLayerShellHandler,
utils::SERIAL_COUNTER,
wayland::shell::wlr_layer::{KeyboardInteractivity, Layer, WlrLayerShellHandler},
};
use wayland_server::protocol::wl_output::WlOutput;
@ -24,9 +25,20 @@ impl WlrLayerShellHandler for State {
.and_then(Output::from_resource)
.unwrap_or_else(|| self.space.outputs().next().unwrap().clone());
let mut map = layer_map_for_output(&output);
map.map_layer(&LayerSurface::new(surface.clone(), namespace))
.unwrap();
let layer_surface = LayerSurface::new(surface.clone(), namespace);
map.map_layer(&layer_surface).unwrap();
surface.send_configure();
let state = layer_surface.cached_state();
if layer_surface.can_receive_keyboard_focus() {
let keyboard = self.seat.get_keyboard().unwrap();
keyboard.set_focus(
self,
Some(surface.wl_surface().clone()),
SERIAL_COUNTER.next_serial(),
);
}
}
fn layer_destroyed(&mut self, surface: smithay::wayland::shell::wlr_layer::LayerSurface) {

View file

@ -2,14 +2,10 @@ use smithay::utils::Point;
use smithay::wayland::seat::WaylandFocus;
use smithay::{
desktop::{
find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output,
PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy, Space,
Window, WindowSurfaceType,
},
input::{
pointer::{Focus, GrabStartData as PointerGrabStartData},
Seat,
find_popup_root_surface, get_popup_toplevel_coords, PopupKeyboardGrab, PopupKind,
PopupManager, PopupPointerGrab, PopupUngrabStrategy, Space, Window,
},
input::{pointer::Focus, Seat},
output::Output,
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
@ -29,11 +25,7 @@ use smithay::{
};
use wayland_server::protocol::wl_output;
use crate::{
grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
render::DecoratedWindow,
state::State,
};
use crate::{state::State, window::DecoratedWindow};
impl XdgShellHandler for State {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
@ -72,35 +64,19 @@ impl XdgShellHandler for State {
}
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
let seat = Seat::from_resource(&seat).unwrap();
let seat: Seat<State> = Seat::from_resource(&seat).unwrap();
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
let pointer = seat.get_pointer().unwrap();
let window = self
.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
let window = self
.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
if window.is_fullscreen() {
return;
}
let initial_window_location = self.space.element_location(&window).unwrap();
let grab = MoveSurfaceGrab {
start_data,
window: window.clone(),
initial_window_location,
};
pointer.set_grab(self, grab, serial, Focus::Clear);
}
window.do_move_grab(self, &seat, &surface, serial, false);
}
fn resize_request(
@ -110,42 +86,19 @@ impl XdgShellHandler for State {
serial: Serial,
edges: xdg_toplevel::ResizeEdge,
) {
let seat = Seat::from_resource(&seat).unwrap();
let seat: Seat<State> = Seat::from_resource(&seat).unwrap();
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
let pointer = seat.get_pointer().unwrap();
let window = self
.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
let window = self
.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
if window.is_fullscreen() {
return;
}
let initial_window_location = self.space.element_location(&window).unwrap();
let initial_window_size = window.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.window.clone(),
edges.into(),
Rectangle::new(initial_window_location, initial_window_size),
);
pointer.set_grab(self, grab, serial, Focus::Clear);
}
window.do_resize_grab(self, &seat, &surface, serial, edges, false);
}
fn grab(&mut self, surface: PopupSurface, seat: wl_seat::WlSeat, serial: Serial) {
@ -342,29 +295,6 @@ impl XdgShellHandler for State {
// fn parent_changed(&mut self, surface: ToplevelSurface) {}
}
fn check_grab(
seat: &Seat<State>,
surface: &WlSurface,
serial: Serial,
) -> Option<PointerGrabStartData<State>> {
let pointer = seat.get_pointer()?;
// Check that this surface has a click grab.
if !pointer.has_grab(serial) {
return None;
}
let start_data = pointer.grab_start_data()?;
let (focus, _) = start_data.focus.as_ref()?;
// If the focus was for a different surface, ignore the request.
if !focus.id().same_client_as(&surface.id()) {
return None;
}
Some(start_data)
}
/// Should be called on `WlSurface::commit`
pub fn handle_commit(popups: &mut PopupManager, space: &Space<Window>, surface: &WlSurface) {
// Handle toplevel commits.

View file

@ -1,14 +1,14 @@
use smithay::desktop::space::SpaceElement;
use smithay::reexports::x11rb::protocol::xkb::SALockControls;
use smithay::{
backend::input::{
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
},
desktop::layer_map_for_output,
input::{
keyboard::FilterResult,
pointer::{AxisFrame, ButtonEvent, MotionEvent},
},
reexports::wayland_server::protocol::wl_surface::WlSurface,
utils::SERIAL_COUNTER,
};
@ -57,29 +57,20 @@ impl State {
}
InputEvent::PointerButton { event, .. } => {
let pointer = self.seat.get_pointer().unwrap();
let keyboard = self.seat.get_keyboard().unwrap();
let serial = SERIAL_COUNTER.next_serial();
let button = event.button_code();
let button_state = event.state();
let seat = self.seat.clone();
let mut dragged_windows = vec![];
if ButtonState::Pressed == button_state && !pointer.is_grabbed() {
let mut actv_window = None;
if let Some((window, _loc)) = self
.space
.element_under(pointer.current_location())
.map(|(w, l)| (w.clone(), l))
{
self.space.raise_element(&window, true);
if let Some(toplevel) = window.window.toplevel() {
keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial);
}
actv_window = Some(window.clone());
} else {
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
}
self.update_focus(serial, &mut actv_window);
self.space.elements().for_each(|window| {
window.window.set_activated(false);
@ -87,21 +78,17 @@ impl State {
if actv_window.window == window.window {
window.window.set_activated(true);
}
}
if let Some(toplevel) = window.window.toplevel() {
toplevel.send_pending_configure();
}
});
self.space.elements().for_each(|window| {
window.window.set_activated(false);
if let Some(actv_window) = actv_window.as_ref() {
if actv_window.window == window.window {
window.window.set_activated(true);
/* window ssd actions */
if window.decorated() {
let mut loc = pointer.current_location();
let window_loc = self.space.element_geometry(window).unwrap();
loc.x -= (window_loc.loc.x - window.bbox().loc.x) as f64;
loc.y -= (window_loc.loc.y - window.bbox().loc.y) as f64;
if window.in_ssd(&loc) {
dragged_windows.push((window.clone(), loc));
}
}
}
if let Some(toplevel) = window.window.toplevel() {
toplevel.send_pending_configure();
}
});
};
@ -115,6 +102,12 @@ impl State {
},
);
pointer.frame(self);
for (window, loc) in &dragged_windows.clone() {
if let Some(toplevel) = window.window.toplevel() {
window.do_ssd_action(self, &seat, toplevel, serial, &loc);
}
}
}
InputEvent::PointerAxis { event, .. } => {
let source = event.source();

View file

@ -3,10 +3,9 @@
mod handlers;
mod audio;
mod grabs;
mod input;
pub mod render;
mod state;
pub mod window;
mod winit;
mod xwayland;

View file

@ -1,8 +1,8 @@
use crate::{audio::SystemAudioPlayer, render::DecoratedWindow};
use crate::{audio::SystemAudioPlayer, window::DecoratedWindow};
use std::{ffi::OsString, sync::Arc};
use smithay::{
desktop::{PopupManager, Space, Window, WindowSurfaceType},
desktop::{layer_map_for_output, PopupManager, Space, Window, WindowSurfaceType},
input::{Seat, SeatState},
reexports::{
calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction},
@ -12,13 +12,16 @@ use smithay::{
Display, DisplayHandle,
},
},
utils::{Logical, Point},
utils::{Logical, Point, Serial},
wayland::{
compositor::{CompositorClientState, CompositorState},
fixes::FixesState,
output::OutputManagerState,
selection::data_device::DataDeviceState,
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
shell::{
wlr_layer::{Layer, WlrLayerShellState},
xdg::{decoration::XdgDecorationState, XdgShellState},
},
shm::ShmState,
socket::ListeningSocketSource,
xdg_activation::XdgActivationState,
@ -53,7 +56,7 @@ pub struct State {
// xdg
pub xdg_shell_state: XdgShellState,
// pub xdg_decoration_state: XdgDecorationState,
pub xdg_decoration_state: XdgDecorationState,
pub xdg_activation_state: XdgActivationState,
pub xdg_toplevel_icon_manager_state: XdgToplevelIconManager,
pub xdg_toplevel_tag_manager_state: XdgToplevelTagManager,
@ -85,7 +88,7 @@ impl State {
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
let xdg_shell_state = XdgShellState::new::<Self>(&dh);
// let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);
let xdg_decoration_state = XdgDecorationState::new::<Self>(&dh);
let xdg_activation_state = XdgActivationState::new::<Self>(&dh);
let xdg_toplevel_icon_manager_state = XdgToplevelIconManager::new::<Self>(&dh);
let xdg_toplevel_tag_manager_state = XdgToplevelTagManager::new::<Self>(&dh);
@ -156,7 +159,7 @@ impl State {
output_manager_state,
wlr_layer_shell_state,
// xdg_decoration_state,
xdg_decoration_state,
xdg_activation_state,
xdg_toplevel_icon_manager_state,
xdg_toplevel_tag_manager_state,
@ -223,6 +226,49 @@ impl State {
})
}
pub fn update_focus(&mut self, serial: Serial, actv_window: &mut Option<DecoratedWindow>) {
/* layer map under? */
let pointer = self.seat.get_pointer().unwrap();
let keyboard = self.seat.get_keyboard().unwrap();
let output = self.space.outputs().next().unwrap().clone();
let map = layer_map_for_output(&output);
let layers_p1 = vec![Layer::Overlay, Layer::Top];
let layers_p2 = vec![Layer::Bottom, Layer::Background];
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
for l in layers_p1 {
if let Some(layer) = map.layer_under(l, pointer.current_location()) {
if layer.can_receive_keyboard_focus() {
keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial);
}
return;
}
}
if let Some((window, _loc)) = self
.space
.element_under(pointer.current_location())
.map(|(w, l)| (w.clone(), l))
{
self.space.raise_element(&window, true);
if let Some(toplevel) = window.window.toplevel() {
keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial);
}
*actv_window = Some(window.clone());
return;
}
for l in layers_p2 {
if let Some(layer) = map.layer_under(l, pointer.current_location()) {
if layer.can_receive_keyboard_focus() {
keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial);
}
}
}
}
pub fn spawn_clients() {
/* todo: read from shit */
std::process::Command::new("startmde").spawn().ok();

View file

@ -1,26 +1,268 @@
//! Resize grab is the state of a composer during which the client window is being resized.
//!
//! eg. Usually whenever a user clicks on the app's border and starts dragging, the compositors
//! enters a ResizeSurfaceGrab state.
use crate::State;
use smithay::input::pointer::GrabStartData as PointerGrabStartData;
use smithay::utils::Rectangle;
use smithay::wayland::shell::xdg::ToplevelSurface;
use smithay::{
desktop::{Space, Window},
utils::Size,
wayland::{compositor, shell::xdg::SurfaceCachedState},
};
use smithay::{
input::pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,
MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,
GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerGrab,
PointerInnerHandle, RelativeMotionEvent,
},
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::protocol::wl_surface::WlSurface,
},
utils::{Logical, Point, Rectangle, Size},
wayland::{compositor, shell::xdg::SurfaceCachedState},
utils::{Logical, Point},
};
use smithay::{
input::{pointer::Focus, Seat},
utils::Serial,
};
use std::cell::RefCell;
use crate::State;
use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayland_server::{protocol::wl_surface::WlSurface, Resource};
use super::DecoratedWindow;
impl DecoratedWindow {
pub fn do_move_grab(
&self,
state: &mut State,
seat: &Seat<State>,
surface: &ToplevelSurface,
serial: Serial,
do_override: bool,
) {
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial, do_override) {
let pointer = seat.get_pointer().unwrap();
if self.is_fullscreen() {
return;
}
let initial_window_location = state.space.element_location(&self).unwrap();
let grab = MoveSurfaceGrab {
start_data,
window: self.clone(),
initial_window_location,
};
pointer.set_grab(state, grab, serial, Focus::Clear);
}
}
pub fn do_resize_grab(
&self,
state: &mut State,
seat: &Seat<State>,
surface: &ToplevelSurface,
serial: Serial,
edges: xdg_toplevel::ResizeEdge,
do_override: bool,
) {
let wl_surface = surface.wl_surface();
if let Some(start_data) = check_grab(&seat, wl_surface, serial, do_override) {
let pointer = seat.get_pointer().unwrap();
if self.is_fullscreen() {
return;
}
let initial_window_location = state.space.element_location(&self).unwrap();
let initial_window_size = self.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,
self.window.clone(),
edges.into(),
Rectangle::new(initial_window_location, initial_window_size),
);
pointer.set_grab(state, grab, serial, Focus::Clear);
}
}
}
fn check_grab(
seat: &Seat<State>,
surface: &WlSurface,
serial: Serial,
do_override: bool,
) -> Option<PointerGrabStartData<State>> {
let pointer = seat.get_pointer()?;
if !do_override {
// Check that this surface has a click grab.
if !pointer.has_grab(serial) {
return None;
}
}
let start_data = pointer.grab_start_data()?;
if !do_override {
let (focus, _) = start_data.focus.as_ref()?;
// If the focus was for a different surface, ignore the request.
if !focus.id().same_client_as(&surface.id()) && !do_override {
return None;
}
}
Some(start_data)
}
pub struct MoveSurfaceGrab {
pub start_data: PointerGrabStartData<State>,
pub window: DecoratedWindow,
pub initial_window_location: Point<i32, Logical>,
}
impl PointerGrab<State> for MoveSurfaceGrab {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(WlSurface, Point<f64, Logical>)>,
event: &MotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
let delta = event.location - self.start_data.location;
let new_location = self.initial_window_location.to_f64() + delta;
data.space
.map_element(self.window.clone(), new_location.to_i32_round(), true);
}
fn relative_motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
focus: Option<(WlSurface, Point<f64, 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);
// The button is a button code as defined in the
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
const BTN_LEFT: u32 = 0x110;
if !handle.current_pressed().contains(&BTN_LEFT) {
// No more buttons are pressed, release the grab.
handle.unset_grab(self, data, event.serial, event.time, true);
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<State> {
&self.start_data
}
fn unset(&mut self, _data: &mut State) {}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ResizeEdge: u32 {

View file

@ -2,13 +2,13 @@
use std::time::Duration;
use crate::State;
use cairo::{Context, Format, ImageSurface};
use parking_lot::Mutex;
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::memory::MemoryRenderBuffer;
use smithay::desktop::layer_map_for_output;
use smithay::desktop::utils::send_frames_surface_tree;
use smithay::utils::{Logical, Transform};
use smithay::wayland::commit_timing::CommitTimerBarrierStateUserData;
use smithay::wayland::seat::WaylandFocus;
use smithay::{
backend::renderer::{
damage::OutputDamageTracker,
@ -26,17 +26,19 @@ use smithay::{
};
use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode;
use self::ssd::{
SSD_BORDER_FRAME_BOTTOM, SSD_BORDER_FRAME_LEFT, SSD_BORDER_FRAME_RIGHT, SSD_BORDER_FRAME_TOP,
};
pub mod grab;
pub mod ssd;
render_elements! {
pub OutputElement<R> where R: ImportMem + ImportDmaWl + ImportMemWl;
Window=WaylandSurfaceRenderElement<R>,
Border=MemoryRenderBufferRenderElement<R>,
}
const CSD_BORDER_FRAME_LEFT: f64 = 5.0;
const CSD_BORDER_FRAME_RIGHT: f64 = 5.0;
const CSD_BORDER_FRAME_TOP: f64 = 23.0;
const CSD_BORDER_FRAME_BOTTOM: f64 = 4.0;
pub struct DecoratedWindow {
pub window: Window,
title: Mutex<String>,
@ -73,7 +75,6 @@ impl Clone for DecoratedWindow {
impl DecoratedWindow {
pub fn new(window: Window, decorations: bool) -> Self {
// let surface = ImageSurface::create(Format::ARgb32, width, height).unwrap();
Self {
window,
title: Mutex::new(String::new()),
@ -104,7 +105,7 @@ impl DecoratedWindow {
*self.fullscreen.lock() = fs;
}
fn decorated(&self) -> bool {
pub fn decorated(&self) -> bool {
let mut b = *self.decorations.lock();
if let Some(toplevel) = self.window.toplevel() {
toplevel.with_pending_state(|f| {
@ -115,179 +116,6 @@ impl DecoratedWindow {
}
b
}
fn render_border(&self, width: i32, height: i32) -> (Vec<u8>, i32) {
let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap();
let stride = surface.stride();
{
let ctx = Context::new(&surface).unwrap(); // scoped — see note below
ctx.set_source_rgba(0.2, 0.2, 0.2, 1.0);
ctx.set_line_width(1.0);
let (w, h) = (width as f64, height as f64);
macro_rules! do_box {
($x:expr, $y:expr, $col:expr, $w:expr, $h:expr) => {
ctx.rectangle(($x) as f64, ($y) as f64, ($w) as f64, ($h) as f64);
ctx.set_source_rgb(($col) as f64, ($col) as f64, ($col) as f64);
ctx.fill().unwrap();
};
}
macro_rules! do_line {
($off:expr, $col:expr) => {
ctx.move_to($off, $off);
ctx.line_to($off, h - $off);
ctx.set_source_rgb($col, $col, $col);
ctx.stroke().unwrap();
ctx.move_to($off, $off);
ctx.line_to(w - $off, $off);
ctx.set_source_rgb($col, $col, $col);
ctx.stroke().unwrap();
};
}
ctx.set_antialias(cairo::Antialias::None);
/* border */
do_box!(0.0, 0.0, 0.14901961, w, h);
do_box!(1.0, 1.0, 0.55686275, w - 2.0, h - 2.0);
do_box!(2.0, 2.0, 0.82352941, w - 4.0, h - 4.0);
do_box!(3.0, 3.0, 0.93333333, w - 6.0, h - 6.0);
do_box!(4.0, 4.0, 1.0, w - 8.0, h - 8.0);
do_box!(4.0, 4.0, 0.14901961, w - 9.0, h - 9.0);
do_line!(1.0, 1.0);
do_line!(2.0, 0.93333333);
do_line!(3.0, 0.82352941);
do_line!(4.0, 0.55686275);
do_line!(5.0, 0.14901961);
for y in 0..18 {
let y = y as f64;
let a = FloatColor {
r: 0.2627451,
g: 0.37254902,
b: 0.49411765,
};
let b = FloatColor {
r: 0.05490196,
g: 0.17254902,
b: 0.30588235,
};
let c = lerp_color(a, b, y / 18.0);
ctx.set_source_rgb(c.r, c.g, c.b);
ctx.move_to(5.0, y + 6.0);
ctx.line_to(w as f64 - 5.0, y + 6.0);
ctx.stroke().unwrap();
}
/* icon */
do_box!(5, 5, 1, 18, 18);
do_box!(6, 6, 0.35294118, 17, 17);
do_box!(6, 6, 0.82352941, 16, 16);
/* close */
do_box!(
w - 18.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 1.0,
1,
18,
18
);
do_box!(
w - 17.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 2.0,
0.35294118,
17,
17
);
do_box!(
w - 17.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 2.0,
0.82352941,
16,
16
);
do_box!(
w - 17.0 - CSD_BORDER_FRAME_LEFT + 5.0,
CSD_BORDER_FRAME_BOTTOM + 7.0,
0,
6,
6
);
do_box!(
w - 17.0 - CSD_BORDER_FRAME_LEFT + 6.0,
CSD_BORDER_FRAME_BOTTOM + 8.0,
0.82352941,
4,
4
);
/* maxim */
do_box!(
w - 36.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 1.0,
1,
18,
18
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 2.0,
0.35294118,
17,
17
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 2.0,
0.82352941,
16,
16
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT + 5.0,
CSD_BORDER_FRAME_BOTTOM + 7.0,
0,
6,
6
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT,
CSD_BORDER_FRAME_BOTTOM + 9.0,
0.82352941,
16,
2
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT + 7.0,
CSD_BORDER_FRAME_BOTTOM + 4.0,
0.82352941,
2,
14
);
do_box!(
w - 35.0 - CSD_BORDER_FRAME_LEFT + 6.0,
CSD_BORDER_FRAME_BOTTOM + 8.0,
0.82352941,
4,
4
);
/* title */
ctx.set_antialias(cairo::Antialias::Best);
ctx.move_to(28.0, 19.0);
ctx.set_font_size(12.0);
ctx.select_font_face("", cairo::FontSlant::Normal, cairo::FontWeight::Normal);
ctx.text_path(&self.title.lock().clone());
ctx.fill().unwrap();
} // ctx dropped here
surface.flush();
(surface.data().unwrap().to_vec(), stride)
}
}
impl SpaceElement for DecoratedWindow {
@ -295,8 +123,10 @@ impl SpaceElement for DecoratedWindow {
let mut bbox = self.window.bbox_with_popups();
if self.decorated() {
bbox.loc.x -= 5;
bbox.loc.y -= 24;
bbox.loc.x -= SSD_BORDER_FRAME_LEFT as i32;
bbox.loc.y -= SSD_BORDER_FRAME_TOP as i32;
bbox.size.w += (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT) as i32;
bbox.size.h += (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM) as i32;
}
bbox
@ -306,7 +136,11 @@ impl SpaceElement for DecoratedWindow {
&self,
point: &smithay::utils::Point<f64, smithay::utils::Logical>,
) -> bool {
self.window.is_in_input_region(point)
if !self.window.is_in_input_region(point) {
return self.in_ssd(point);
} else {
return true;
}
}
fn set_activate(&self, activated: bool) {
@ -326,6 +160,12 @@ impl SpaceElement for DecoratedWindow {
*self.output.lock() = None;
self.window.output_leave(output)
}
fn z_index(&self) -> u8 {
smithay::desktop::space::RenderZindex::Overlay as u8
}
fn refresh(&self) {}
}
impl IsAlive for DecoratedWindow {
@ -373,7 +213,7 @@ where
let width = bg_rect.size.w;
let height = bg_rect.size.h;
let data = self.render_border(width, height);
let data = self.render_csd(width, height);
let buffer = MemoryRenderBuffer::from_slice(
&data.0,
Fourcc::Argb8888,
@ -400,19 +240,6 @@ where
}
}
struct FloatColor {
r: f64,
g: f64,
b: f64,
}
fn lerp_color(a: FloatColor, b: FloatColor, t: f64) -> FloatColor {
return FloatColor {
r: a.r + (b.r - a.r) * t,
g: a.g + (b.g - a.g) * t,
b: a.b + (b.b - a.b) * t,
};
}
impl State {
pub fn render(
&self,
@ -422,12 +249,15 @@ impl State {
damage_tracker: &mut OutputDamageTracker,
) -> Result<(), Box<dyn std::error::Error>> {
self.space.elements().for_each(|window| {
window.window.send_frame(
&output,
self.start_time.elapsed(),
Some(Duration::ZERO),
|_, _| Some(output.clone()),
)
if let Some(surface) = window.window.wl_surface() {
send_frames_surface_tree(
&surface,
&output,
self.start_time.elapsed(),
Some(Duration::ZERO),
|_, _| Some(output.clone()),
);
}
});
self.space.outputs().for_each(|output| {
@ -435,13 +265,13 @@ impl State {
map.arrange();
map.layers().for_each(|layer| {
layer.layer_surface().send_pending_configure();
layer.send_frame(
send_frames_surface_tree(
layer.wl_surface(),
&output,
self.start_time.elapsed(),
Some(Duration::ZERO),
|_, _| Some(output.clone()),
)
);
})
});

314
src/window/ssd.rs Normal file
View file

@ -0,0 +1,314 @@
use cairo::{Context, Format, ImageSurface};
use smithay::{
input::Seat,
output::Scale,
utils::{Logical, Point, Serial},
wayland::shell::xdg::ToplevelSurface,
};
use wayland_protocols::xdg::shell::server::xdg_toplevel;
use crate::State;
use super::DecoratedWindow;
pub const SSD_BORDER_FRAME_LEFT: f64 = 5.0;
pub const SSD_BORDER_FRAME_RIGHT: f64 = 5.0;
pub const SSD_BORDER_FRAME_TOP: f64 = 23.0;
pub const SSD_BORDER_FRAME_BOTTOM: f64 = 4.0;
struct FloatColor {
r: f64,
g: f64,
b: f64,
}
fn lerp_color(a: FloatColor, b: FloatColor, t: f64) -> FloatColor {
return FloatColor {
r: a.r + (b.r - a.r) * t,
g: a.g + (b.g - a.g) * t,
b: a.b + (b.b - a.b) * t,
};
}
impl DecoratedWindow {
pub fn in_ssd(&self, point: &Point<f64, Logical>) -> bool {
let loc = self.window.bbox();
return (point.x < loc.loc.x as f64 && point.x > loc.loc.x as f64 - SSD_BORDER_FRAME_LEFT)
|| (point.x > loc.size.w as f64
&& point.x
< (loc.size.w as f64 + (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT)))
|| point.y < loc.loc.y as f64 && point.y > loc.loc.y as f64 - SSD_BORDER_FRAME_TOP
|| (point.y > loc.size.h as f64
&& point.y
< (loc.size.h as f64 + (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM)));
}
pub fn do_ssd_action(
&self,
state: &mut State,
seat: &Seat<State>,
surface: &ToplevelSurface,
serial: Serial,
point: &Point<f64, Logical>,
) {
let loc = self.window.bbox();
/* leftmost */
let leftmost =
point.x < loc.loc.x as f64 && point.x > loc.loc.x as f64 - SSD_BORDER_FRAME_LEFT;
let rightmost = point.x > loc.size.w as f64
&& point.x < (loc.size.w as f64 + (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT));
let topmost =
point.y < loc.loc.y as f64 && point.y > loc.loc.y as f64 - SSD_BORDER_FRAME_TOP;
let bottommost = point.y > loc.size.h as f64
&& point.y < (loc.size.h as f64 + (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM));
if leftmost {
self.do_resize_grab(
state,
seat,
surface,
serial,
{
if topmost {
xdg_toplevel::ResizeEdge::TopLeft
} else if bottommost {
xdg_toplevel::ResizeEdge::BottomLeft
} else {
xdg_toplevel::ResizeEdge::Left
}
},
true,
)
}
/* rightmost */
else if rightmost {
self.do_resize_grab(
state,
seat,
surface,
serial,
if topmost {
xdg_toplevel::ResizeEdge::TopRight
} else if bottommost {
xdg_toplevel::ResizeEdge::BottomRight
} else {
xdg_toplevel::ResizeEdge::Right
},
true,
)
}
/* topmost */
else if topmost {
/* title bar */
if point.y < loc.loc.y as f64
&& point.y > loc.loc.y as f64 - (SSD_BORDER_FRAME_TOP - 5.0)
{
self.do_move_grab(state, seat, surface, serial, true)
} else {
/* topmost edge */
self.do_resize_grab(
state,
seat,
surface,
serial,
if leftmost {
xdg_toplevel::ResizeEdge::TopLeft
} else if rightmost {
xdg_toplevel::ResizeEdge::TopRight
} else {
xdg_toplevel::ResizeEdge::Top
},
true,
);
}
}
/* bottommost */
else if bottommost {
self.do_resize_grab(
state,
seat,
surface,
serial,
if leftmost {
xdg_toplevel::ResizeEdge::BottomLeft
} else if rightmost {
xdg_toplevel::ResizeEdge::BottomRight
} else {
xdg_toplevel::ResizeEdge::Bottom
},
true,
)
}
}
pub fn render_csd(&self, width: i32, height: i32) -> (Vec<u8>, i32) {
let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap();
let stride = surface.stride();
{
let ctx = Context::new(&surface).unwrap(); // scoped — see note below
ctx.set_source_rgba(0.2, 0.2, 0.2, 1.0);
ctx.set_line_width(1.0);
let (w, h) = (width as f64, height as f64);
macro_rules! do_box {
($x:expr, $y:expr, $col:expr, $w:expr, $h:expr) => {
ctx.rectangle(($x) as f64, ($y) as f64, ($w) as f64, ($h) as f64);
ctx.set_source_rgb(($col) as f64, ($col) as f64, ($col) as f64);
ctx.fill().unwrap();
};
}
macro_rules! do_line {
($off:expr, $col:expr) => {
ctx.move_to($off, $off);
ctx.line_to($off, h - $off);
ctx.set_source_rgb($col, $col, $col);
ctx.stroke().unwrap();
ctx.move_to($off, $off);
ctx.line_to(w - $off, $off);
ctx.set_source_rgb($col, $col, $col);
ctx.stroke().unwrap();
};
}
ctx.set_antialias(cairo::Antialias::None);
/* border */
do_box!(0.0, 0.0, 0.14901961, w, h);
do_box!(1.0, 1.0, 0.55686275, w - 2.0, h - 2.0);
do_box!(2.0, 2.0, 0.82352941, w - 4.0, h - 4.0);
do_box!(3.0, 3.0, 0.93333333, w - 6.0, h - 6.0);
do_box!(4.0, 4.0, 1.0, w - 8.0, h - 8.0);
do_box!(4.0, 4.0, 0.14901961, w - 9.0, h - 9.0);
do_line!(1.0, 1.0);
do_line!(2.0, 0.93333333);
do_line!(3.0, 0.82352941);
do_line!(4.0, 0.55686275);
do_line!(5.0, 0.14901961);
for y in 0..18 {
let y = y as f64;
let a = FloatColor {
r: 0.2627451,
g: 0.37254902,
b: 0.49411765,
};
let b = FloatColor {
r: 0.05490196,
g: 0.17254902,
b: 0.30588235,
};
let c = lerp_color(a, b, y / 18.0);
ctx.set_source_rgb(c.r, c.g, c.b);
ctx.move_to(5.0, y + 6.0);
ctx.line_to(w as f64 - 5.0, y + 6.0);
ctx.stroke().unwrap();
}
/* icon */
do_box!(5, 5, 1, 18, 18);
do_box!(6, 6, 0.35294118, 17, 17);
do_box!(6, 6, 0.82352941, 16, 16);
/* close */
do_box!(
w - 18.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 1.0,
1,
18,
18
);
do_box!(
w - 17.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 2.0,
0.35294118,
17,
17
);
do_box!(
w - 17.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 2.0,
0.82352941,
16,
16
);
do_box!(
w - 17.0 - SSD_BORDER_FRAME_LEFT + 5.0,
SSD_BORDER_FRAME_BOTTOM + 7.0,
0,
6,
6
);
do_box!(
w - 17.0 - SSD_BORDER_FRAME_LEFT + 6.0,
SSD_BORDER_FRAME_BOTTOM + 8.0,
0.82352941,
4,
4
);
/* maxim */
do_box!(
w - 36.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 1.0,
1,
18,
18
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 2.0,
0.35294118,
17,
17
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 2.0,
0.82352941,
16,
16
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT + 5.0,
SSD_BORDER_FRAME_BOTTOM + 7.0,
0,
6,
6
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT,
SSD_BORDER_FRAME_BOTTOM + 9.0,
0.82352941,
16,
2
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT + 7.0,
SSD_BORDER_FRAME_BOTTOM + 4.0,
0.82352941,
2,
14
);
do_box!(
w - 35.0 - SSD_BORDER_FRAME_LEFT + 6.0,
SSD_BORDER_FRAME_BOTTOM + 8.0,
0.82352941,
4,
4
);
/* title */
ctx.set_antialias(cairo::Antialias::Best);
ctx.move_to(28.0, 19.0);
ctx.set_font_size(12.0);
ctx.select_font_face("", cairo::FontSlant::Normal, cairo::FontWeight::Normal);
ctx.text_path(&self.title.lock().clone());
ctx.fill().unwrap();
} // ctx dropped here
surface.flush();
(surface.data().unwrap().to_vec(), stride)
}
}

View file

@ -6,7 +6,7 @@ use smithay::{
winit::{self, WinitEvent},
},
output::{Mode, Output, PhysicalProperties, Subpixel},
utils::{Rectangle, Transform},
utils::{Rectangle, Transform, SERIAL_COUNTER},
};
use crate::State;

View file

@ -12,7 +12,7 @@ use smithay::{
};
use wayland_server::DisplayHandle;
use crate::{render::DecoratedWindow, state::State};
use crate::{state::State, window::DecoratedWindow};
pub struct XWaylandState {
pub xwm: Option<X11Wm>,