refactor(wayland): use smithay xdg_shell handler
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2137,7 +2137,6 @@ dependencies = [
|
||||
"directories",
|
||||
"fxtypemap",
|
||||
"glam 0.23.0",
|
||||
"global_counter",
|
||||
"input-event-codes",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
||||
@@ -60,7 +60,6 @@ serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_repr = "0.1.16"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
global_counter = "0.2.2"
|
||||
rand = "0.8.5"
|
||||
atty = "0.2.14"
|
||||
xkbcommon = { version = "0.7.0", default-features = false, optional = true }
|
||||
|
||||
@@ -22,7 +22,6 @@ use self::xwayland_rootless::XWaylandState;
|
||||
use self::{state::WaylandState, surface::CORE_SURFACES};
|
||||
use crate::{core::task, wayland::state::ClientState};
|
||||
use color_eyre::eyre::{ensure, Result};
|
||||
use global_counter::primitive::exact::CounterU32;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use sk::StereoKitDraw;
|
||||
@@ -52,7 +51,6 @@ use tracing::{debug_span, info, instrument};
|
||||
|
||||
pub static X_DISPLAY: OnceCell<u32> = OnceCell::new();
|
||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
||||
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
||||
|
||||
struct EGLRawHandles {
|
||||
display: *const c_void,
|
||||
@@ -175,6 +173,7 @@ impl Wayland {
|
||||
acc = listen_async.accept() => { // New client connected
|
||||
let (stream, _) = acc?;
|
||||
let client_state = Arc::new(ClientState {
|
||||
pid: stream.peer_cred().ok().and_then(|c| c.pid()),
|
||||
id: OnceCell::new(),
|
||||
compositor_state: Default::default(),
|
||||
display: Arc::downgrade(&display),
|
||||
|
||||
@@ -106,15 +106,49 @@ impl SeatWrapper {
|
||||
touches: Mutex::new(FxHashMap::default()),
|
||||
}
|
||||
}
|
||||
pub fn unfocus(&self, surface: &WlSurface, state: &mut WaylandState) {
|
||||
let pointer = self.seat.get_pointer().unwrap();
|
||||
if pointer.current_focus() == Some(surface.clone()) {
|
||||
pointer.motion(
|
||||
state,
|
||||
None,
|
||||
&MotionEvent {
|
||||
location: (0.0, 0.0).into(),
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
let keyboard = self.seat.get_keyboard().unwrap();
|
||||
if keyboard.current_focus() == Some(surface.clone()) {
|
||||
keyboard.set_focus(state, None, SERIAL_COUNTER.next_serial());
|
||||
}
|
||||
let touch = self.seat.get_touch().unwrap();
|
||||
for (id, touch_surface) in self.touches.lock().iter() {
|
||||
if touch_surface.id() == surface.id() {
|
||||
self.touch_up(*id);
|
||||
touch.up(
|
||||
state,
|
||||
&UpEvent {
|
||||
slot: Some(*id).into(),
|
||||
serial: SERIAL_COUNTER.next_serial(),
|
||||
time: 0,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_motion(&self, surface: WlSurface, position: Vector2<f32>) {
|
||||
let Some(state) = self.wayland_state.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let mut state = state.lock();
|
||||
let Some(pointer) = self.seat.get_pointer() else {
|
||||
return;
|
||||
};
|
||||
pointer.motion(
|
||||
&mut state.lock(),
|
||||
&mut state,
|
||||
Some((surface, (0, 0).into())),
|
||||
&MotionEvent {
|
||||
location: (position.x as f64, position.y as f64).into(),
|
||||
@@ -122,16 +156,18 @@ impl SeatWrapper {
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(&mut state);
|
||||
}
|
||||
pub fn pointer_button(&self, button: u32, pressed: bool) {
|
||||
let Some(state) = self.wayland_state.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let mut state = state.lock();
|
||||
let Some(pointer) = self.seat.get_pointer() else {
|
||||
return;
|
||||
};
|
||||
pointer.button(
|
||||
&mut state.lock(),
|
||||
&mut state,
|
||||
&ButtonEvent {
|
||||
button,
|
||||
state: if pressed {
|
||||
@@ -143,6 +179,7 @@ impl SeatWrapper {
|
||||
time: 0,
|
||||
},
|
||||
);
|
||||
pointer.frame(&mut state);
|
||||
}
|
||||
pub fn pointer_scroll(
|
||||
&self,
|
||||
@@ -171,7 +208,8 @@ impl SeatWrapper {
|
||||
v120: scroll_steps.map(|d| ((d.x * 120.0) as i32, (d.y * 120.0) as i32)),
|
||||
stop: (false, false),
|
||||
},
|
||||
)
|
||||
);
|
||||
pointer.frame(&mut state);
|
||||
}
|
||||
|
||||
pub fn keyboard_keys(&self, surface: WlSurface, keymap_id: &str, keys: Vec<i32>) {
|
||||
|
||||
@@ -1,606 +0,0 @@
|
||||
use super::{
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
SERIAL_COUNTER,
|
||||
};
|
||||
use crate::{
|
||||
core::task,
|
||||
nodes::items::panel::{Backend, Geometry, PanelItem},
|
||||
};
|
||||
use color_eyre::eyre::{bail, eyre, Result};
|
||||
use mint::Vector2;
|
||||
use nanoid::nanoid;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use rand::{seq::IteratorRandom, thread_rng};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smithay::{
|
||||
input::keyboard::{KeymapFile, ModifiersState},
|
||||
reexports::wayland_server::{
|
||||
backend::{ClientId, GlobalId, ObjectId},
|
||||
protocol::{
|
||||
wl_keyboard::{self, KeyState, WlKeyboard},
|
||||
wl_pointer::{self, Axis, ButtonState, WlPointer},
|
||||
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
|
||||
wl_surface::WlSurface,
|
||||
wl_touch::{self, WlTouch},
|
||||
},
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak,
|
||||
},
|
||||
wayland::compositor,
|
||||
};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::sync::watch;
|
||||
use tracing::{debug, warn};
|
||||
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keycode, Keymap};
|
||||
|
||||
pub fn handle_cursor<B: Backend>(
|
||||
panel_item: &Arc<PanelItem<B>>,
|
||||
mut cursor: watch::Receiver<Option<CursorInfo>>,
|
||||
) {
|
||||
let panel_item_weak = Arc::downgrade(panel_item);
|
||||
let _ = task::new(|| "cursor handler", async move {
|
||||
while cursor.changed().await.is_ok() {
|
||||
let Some(panel_item) = panel_item_weak.upgrade() else {continue};
|
||||
let cursor_info = cursor.borrow();
|
||||
panel_item.set_cursor(cursor_info.as_ref().and_then(CursorInfo::cursor_data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub struct KeyboardInfo {
|
||||
keymap_string: String,
|
||||
keymap: KeymapFile,
|
||||
state: xkb::State,
|
||||
mods: ModifiersState,
|
||||
keys: FxHashSet<u32>,
|
||||
}
|
||||
impl KeyboardInfo {
|
||||
pub fn new(keymap_string: String, keymap: &Keymap) -> Self {
|
||||
KeyboardInfo {
|
||||
keymap_string,
|
||||
state: xkb::State::new(keymap),
|
||||
keymap: KeymapFile::new(keymap),
|
||||
mods: ModifiersState::default(),
|
||||
keys: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
pub fn process(&mut self, key: u32, pressed: bool, keyboard: &WlKeyboard) -> Result<usize> {
|
||||
let xkb_key_state = if pressed {
|
||||
xkb::KeyDirection::Down
|
||||
} else {
|
||||
xkb::KeyDirection::Up
|
||||
};
|
||||
let state_components = self.state.update_key(Keycode::new(key + 8), xkb_key_state);
|
||||
if state_components != 0 {
|
||||
self.mods.update_with(&self.state);
|
||||
keyboard.modifiers(
|
||||
0,
|
||||
self.mods.serialized.depressed,
|
||||
self.mods.serialized.latched,
|
||||
self.mods.serialized.locked,
|
||||
0,
|
||||
);
|
||||
}
|
||||
// if pressed {
|
||||
// println!("Key {key} is being pressed with {state_components} modifiers");
|
||||
// } else {
|
||||
// println!("Key {key} is being released with {state_components} modifiers");
|
||||
// }
|
||||
|
||||
let wl_key_state = if pressed {
|
||||
KeyState::Pressed
|
||||
} else {
|
||||
KeyState::Released
|
||||
};
|
||||
keyboard.key(SERIAL_COUNTER.inc(), 0, key, wl_key_state);
|
||||
match wl_key_state {
|
||||
KeyState::Pressed => {
|
||||
self.keys.insert(key);
|
||||
}
|
||||
KeyState::Released => {
|
||||
self.keys.remove(&key);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
Ok(self.keys.len())
|
||||
}
|
||||
}
|
||||
unsafe impl Send for KeyboardInfo {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PointerEvent {
|
||||
Motion(Vector2<f32>),
|
||||
Button {
|
||||
button: u32,
|
||||
state: u32,
|
||||
},
|
||||
Scroll {
|
||||
axis_continuous: Option<Vector2<f32>>,
|
||||
axis_discrete: Option<Vector2<f32>>,
|
||||
},
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum KeyboardEvent {
|
||||
Keymap,
|
||||
Key { key: u32, state: bool },
|
||||
}
|
||||
|
||||
const POINTER_EVENT_TIMEOUT: Duration = Duration::from_millis(50);
|
||||
struct SurfaceInfo {
|
||||
wl_surface: WlWeak<WlSurface>,
|
||||
cursor_sender: watch::Sender<Option<CursorInfo>>,
|
||||
pointer_queue: VecDeque<PointerEvent>,
|
||||
pointer_latest_event: Instant,
|
||||
keyboard_queue: VecDeque<KeyboardEvent>,
|
||||
keyboard_info: Option<KeyboardInfo>,
|
||||
}
|
||||
impl SurfaceInfo {
|
||||
fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender<Option<CursorInfo>>) -> Self {
|
||||
SurfaceInfo {
|
||||
wl_surface: wl_surface.downgrade(),
|
||||
cursor_sender,
|
||||
pointer_queue: VecDeque::new(),
|
||||
pointer_latest_event: Instant::now(),
|
||||
keyboard_queue: VecDeque::new(),
|
||||
keyboard_info: None,
|
||||
}
|
||||
}
|
||||
fn flush(&self) {
|
||||
if let Some(client) = self.wl_surface.upgrade().ok().and_then(|s| s.client()) {
|
||||
if let Some(client_data) = client.get_data::<ClientState>() {
|
||||
client_data.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool {
|
||||
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; };
|
||||
let Some(focus_size) = core_surface.size() else { return false; };
|
||||
|
||||
if !self.pointer_queue.is_empty() {
|
||||
self.pointer_latest_event = Instant::now();
|
||||
}
|
||||
while let Some(event) = self.pointer_queue.pop_front() {
|
||||
match (locked, event) {
|
||||
(false, PointerEvent::Motion(pos)) => {
|
||||
pointer.enter(
|
||||
SERIAL_COUNTER.inc(),
|
||||
&focus,
|
||||
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
||||
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
||||
);
|
||||
locked = true;
|
||||
}
|
||||
(true, PointerEvent::Motion(pos)) => {
|
||||
pointer.motion(
|
||||
0,
|
||||
(pos.x as f64).clamp(0.0, focus_size.x as f64),
|
||||
(pos.y as f64).clamp(0.0, focus_size.y as f64),
|
||||
);
|
||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
||||
pointer.frame();
|
||||
}
|
||||
}
|
||||
(true, PointerEvent::Button { button, state }) => {
|
||||
pointer.button(
|
||||
0,
|
||||
0,
|
||||
button,
|
||||
match state {
|
||||
0 => ButtonState::Released,
|
||||
1 => ButtonState::Pressed,
|
||||
_ => continue,
|
||||
},
|
||||
);
|
||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
||||
pointer.frame();
|
||||
}
|
||||
}
|
||||
(
|
||||
true,
|
||||
PointerEvent::Scroll {
|
||||
axis_continuous,
|
||||
axis_discrete,
|
||||
},
|
||||
) => {
|
||||
if let Some(axis_continuous) = axis_continuous {
|
||||
pointer.axis(0, Axis::HorizontalScroll, axis_continuous.x as f64);
|
||||
pointer.axis(0, Axis::VerticalScroll, -axis_continuous.y as f64);
|
||||
}
|
||||
if pointer.version() >= wl_pointer::EVT_AXIS_DISCRETE_SINCE {
|
||||
if let Some(axis_discrete) = axis_discrete {
|
||||
pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete.x as i32);
|
||||
pointer.axis_discrete(Axis::VerticalScroll, -axis_discrete.y as i32);
|
||||
}
|
||||
}
|
||||
if pointer.version() >= wl_pointer::EVT_AXIS_STOP_SINCE
|
||||
&& axis_discrete.is_none()
|
||||
&& axis_continuous.is_none()
|
||||
{
|
||||
pointer.axis_stop(0, Axis::HorizontalScroll);
|
||||
pointer.axis_stop(0, Axis::VerticalScroll);
|
||||
}
|
||||
if pointer.version() >= wl_pointer::EVT_FRAME_SINCE {
|
||||
pointer.frame();
|
||||
}
|
||||
}
|
||||
(locked, event) => {
|
||||
warn!(locked, ?event, "Invalid pointer event!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.pointer_latest_event.elapsed() > POINTER_EVENT_TIMEOUT {
|
||||
pointer.leave(SERIAL_COUNTER.inc(), &focus);
|
||||
locked = false;
|
||||
}
|
||||
self.flush();
|
||||
|
||||
locked
|
||||
}
|
||||
fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool {
|
||||
let Ok(focus) = self.wl_surface.upgrade() else { return false; };
|
||||
let Some(info) = self.keyboard_info.as_mut() else { return true; };
|
||||
|
||||
if !locked {
|
||||
keyboard.enter(0, &focus, vec![]);
|
||||
if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE {
|
||||
keyboard.repeat_info(0, 0);
|
||||
}
|
||||
locked = info.keymap.send(keyboard).is_ok();
|
||||
}
|
||||
while let Some(event) = self.keyboard_queue.pop_front() {
|
||||
debug!(locked, ?event, "Process keyboard event");
|
||||
match (locked, event) {
|
||||
(true, KeyboardEvent::Keymap) => {
|
||||
let _ = info.keymap.send(keyboard);
|
||||
}
|
||||
(true, KeyboardEvent::Key { key, state }) => {
|
||||
if let Ok(key_count) = info.process(key, state, keyboard) {
|
||||
if key_count == 0 {
|
||||
keyboard.leave(SERIAL_COUNTER.inc(), &focus);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
(locked, event) => {
|
||||
warn!(locked, ?event, "Invalid keyboard event!");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.flush();
|
||||
locked
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SeatData {
|
||||
pub client: OnceCell<ClientId>,
|
||||
global_id: OnceCell<GlobalId>,
|
||||
surfaces: Mutex<FxHashMap<ObjectId, SurfaceInfo>>,
|
||||
pointer: OnceCell<(WlPointer, Mutex<ObjectId>)>,
|
||||
keyboard: OnceCell<(WlKeyboard, Mutex<ObjectId>)>,
|
||||
touch: OnceCell<WlTouch>,
|
||||
touches: Mutex<FxHashMap<ObjectId, u32>>,
|
||||
}
|
||||
impl SeatData {
|
||||
pub fn new(dh: &DisplayHandle) -> Arc<Self> {
|
||||
let seat_data = Arc::new(SeatData {
|
||||
client: OnceCell::new(),
|
||||
global_id: OnceCell::new(),
|
||||
surfaces: Mutex::new(FxHashMap::default()),
|
||||
pointer: OnceCell::new(),
|
||||
keyboard: OnceCell::new(),
|
||||
touch: OnceCell::new(),
|
||||
touches: Mutex::new(FxHashMap::default()),
|
||||
});
|
||||
|
||||
let _ = seat_data
|
||||
.global_id
|
||||
.set(dh.create_global::<WaylandState, _, _>(7, seat_data.clone()));
|
||||
|
||||
seat_data
|
||||
}
|
||||
|
||||
pub fn set_keymap(&self, keymap_str: String, surfaces: Vec<WlSurface>) -> Result<()> {
|
||||
let context = xkb::Context::new(0);
|
||||
let keymap =
|
||||
Keymap::new_from_string(&context, keymap_str.clone(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
|
||||
.ok_or_else(|| eyre!("Keymap is not valid"))?;
|
||||
let mut panels = self.surfaces.lock();
|
||||
let Some((_, focus)) = self.keyboard.get() else {bail!("Could not get keyboard")};
|
||||
for surface in surfaces {
|
||||
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
|
||||
if let Some(keyboard_info) = &mut surface_info.keyboard_info {
|
||||
if &keyboard_info.keymap_string == &keymap_str {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
surface_info
|
||||
.keyboard_info
|
||||
.replace(KeyboardInfo::new(keymap_str.clone(), &keymap));
|
||||
|
||||
if *focus.lock() == surface.id() {
|
||||
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) {
|
||||
let mut surfaces = self.surfaces.lock();
|
||||
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
|
||||
surface_info.pointer_queue.push_back(event);
|
||||
drop(surfaces);
|
||||
self.handle_pointer_events();
|
||||
}
|
||||
pub fn keyboard_event(&self, surface: &WlSurface, event: KeyboardEvent) {
|
||||
let mut surfaces = self.surfaces.lock();
|
||||
let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return};
|
||||
surface_info.keyboard_queue.push_back(event);
|
||||
drop(surfaces);
|
||||
self.handle_keyboard_events();
|
||||
}
|
||||
|
||||
fn handle_pointer_events(&self) {
|
||||
let mut surfaces = self.surfaces.lock();
|
||||
let Some((pointer, pointer_focus)) = self.pointer.get() else {return};
|
||||
let mut pointer_focus = pointer_focus.lock();
|
||||
|
||||
loop {
|
||||
let locked = !pointer_focus.is_null();
|
||||
// Pick a pointer to focus on if there is none
|
||||
if pointer_focus.is_null() {
|
||||
*pointer_focus = surfaces
|
||||
.iter()
|
||||
.filter(|(_k, v)| !v.pointer_queue.is_empty())
|
||||
.map(|(k, _v)| k)
|
||||
.choose(&mut thread_rng())
|
||||
.cloned()
|
||||
.unwrap_or(ObjectId::null());
|
||||
}
|
||||
if pointer_focus.is_null() {
|
||||
// If there's still none, guess we're done with pointer events for the time being
|
||||
break;
|
||||
}
|
||||
let Some(surface_info) = surfaces.get_mut(&pointer_focus) else {break};
|
||||
if surface_info.handle_pointer_events(pointer, locked) {
|
||||
// We haven't gotten to a point where we can switch the focus
|
||||
break;
|
||||
} else {
|
||||
*pointer_focus = ObjectId::null();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn handle_keyboard_events(&self) {
|
||||
let mut surfaces = self.surfaces.lock();
|
||||
let Some((keyboard, keyboard_focus)) = self.keyboard.get() else {return};
|
||||
let mut keyboard_focus = keyboard_focus.lock();
|
||||
loop {
|
||||
let locked = !keyboard_focus.is_null();
|
||||
// Pick a keyboard to focus on if there is none
|
||||
if keyboard_focus.is_null() {
|
||||
*keyboard_focus = surfaces
|
||||
.iter()
|
||||
.filter(|(_k, v)| v.keyboard_info.is_some())
|
||||
.filter(|(_k, v)| !v.keyboard_queue.is_empty())
|
||||
.map(|(k, _v)| k)
|
||||
.choose(&mut thread_rng())
|
||||
.cloned()
|
||||
.unwrap_or(ObjectId::null());
|
||||
}
|
||||
// If there's still none, guess we're done with keyboard events for the time being
|
||||
let Some(surface_info) = surfaces.get_mut(&keyboard_focus) else {break};
|
||||
if surface_info.handle_keyboard_events(keyboard, locked) {
|
||||
// We haven't gotten to a point where we can switch the focus
|
||||
break;
|
||||
} else {
|
||||
*keyboard_focus = ObjectId::null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_surface(&self, surface: &WlSurface) -> watch::Receiver<Option<CursorInfo>> {
|
||||
let (tx, rx) = watch::channel(None);
|
||||
self.surfaces
|
||||
.lock()
|
||||
.insert(surface.id(), SurfaceInfo::new(surface, tx));
|
||||
|
||||
rx
|
||||
}
|
||||
pub fn drop_surface(&self, surface: &WlSurface) {
|
||||
self.surfaces.lock().remove(&surface.id());
|
||||
if let Some((_, pointer_focus)) = self.pointer.get() {
|
||||
let mut pointer_focus = pointer_focus.lock();
|
||||
if *pointer_focus == surface.id() {
|
||||
*pointer_focus = ObjectId::null();
|
||||
}
|
||||
}
|
||||
if let Some((_, keyboard_focus)) = self.keyboard.get() {
|
||||
let mut keyboard_focus = keyboard_focus.lock();
|
||||
if *keyboard_focus == surface.id() {
|
||||
*keyboard_focus = ObjectId::null();
|
||||
}
|
||||
}
|
||||
self.touches.lock().remove(&surface.id());
|
||||
}
|
||||
|
||||
pub fn touch_down(&self, surface: &WlSurface, id: u32, position: Vector2<f32>) {
|
||||
let Some(touch) = self.touch.get() else {return};
|
||||
touch.down(
|
||||
SERIAL_COUNTER.inc(),
|
||||
0,
|
||||
surface,
|
||||
id as i32,
|
||||
position.x as f64,
|
||||
position.y as f64,
|
||||
);
|
||||
self.touches.lock().insert(surface.id(), id);
|
||||
}
|
||||
pub fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
||||
let Some(touch) = self.touch.get() else {return};
|
||||
touch.motion(0, id as i32, position.x as f64, position.y as f64);
|
||||
}
|
||||
pub fn touch_up(&self, id: u32) {
|
||||
let Some(touch) = self.touch.get() else {return};
|
||||
touch.up(SERIAL_COUNTER.inc(), 0, id as i32);
|
||||
let mut touches = self.touches.lock();
|
||||
touches.retain(|_, tid| *tid != id);
|
||||
}
|
||||
pub fn reset_touches(&self) {
|
||||
let Some(touch) = self.touch.get() else {return};
|
||||
for (_, touch_id) in self.touches.lock().drain() {
|
||||
touch.up(SERIAL_COUNTER.inc(), 0, touch_id as i32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CursorInfo {
|
||||
pub surface: WlWeak<WlSurface>,
|
||||
pub hotspot_x: i32,
|
||||
pub hotspot_y: i32,
|
||||
}
|
||||
impl CursorInfo {
|
||||
pub fn cursor_data(&self) -> Option<Geometry> {
|
||||
let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?;
|
||||
Some(Geometry {
|
||||
origin: [self.hotspot_x, self.hotspot_y].into(),
|
||||
size: cursor_size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalDispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
fn bind(
|
||||
_state: &mut WaylandState,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<WlSeat>,
|
||||
data: &Arc<SeatData>,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let resource = data_init.init(resource, data.clone());
|
||||
|
||||
if resource.version() >= EVT_NAME_SINCE {
|
||||
resource.name(nanoid!());
|
||||
}
|
||||
|
||||
resource.capabilities(Capability::Pointer | Capability::Keyboard | Capability::Touch);
|
||||
}
|
||||
|
||||
fn can_view(client: Client, data: &Arc<SeatData>) -> bool {
|
||||
let Some(seat_client) = data.client.get().cloned() else {return false};
|
||||
client.id() == seat_client
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlSeat, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &WlSeat,
|
||||
request: wl_seat::Request,
|
||||
data: &Arc<SeatData>,
|
||||
_dh: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
wl_seat::Request::GetPointer { id } => {
|
||||
let pointer = data_init.init(id, data.clone());
|
||||
let _ = data.pointer.set((pointer, Mutex::new(ObjectId::null())));
|
||||
}
|
||||
wl_seat::Request::GetKeyboard { id } => {
|
||||
let keyboard = data_init.init(id, data.clone());
|
||||
if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE {
|
||||
keyboard.repeat_info(0, 0);
|
||||
}
|
||||
let _ = data.keyboard.set((keyboard, Mutex::new(ObjectId::null())));
|
||||
}
|
||||
wl_seat::Request::GetTouch { id } => {
|
||||
let _ = data.touch.set(data_init.init(id, data.clone()));
|
||||
}
|
||||
wl_seat::Request::Release => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlPointer, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &WlPointer,
|
||||
request: wl_pointer::Request,
|
||||
seat_data: &Arc<SeatData>,
|
||||
dh: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
wl_pointer::Request::SetCursor {
|
||||
serial: _,
|
||||
surface,
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
} => {
|
||||
if let Some(surface) = surface.as_ref() {
|
||||
CoreSurface::add_to(dh.clone(), surface, || (), |_| ());
|
||||
compositor::with_states(surface, |data| {
|
||||
if let Some(core_surface) = data.data_map.get::<Arc<CoreSurface>>() {
|
||||
core_surface.set_material_offset(1);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let Some((_, focus)) = seat_data.pointer.get() else {return};
|
||||
let focus = focus.lock();
|
||||
let surfaces = seat_data.surfaces.lock();
|
||||
let Some(surface_info) = surfaces.get(&focus) else {return};
|
||||
let cursor_info = surface.map(|surface| CursorInfo {
|
||||
surface: surface.downgrade(),
|
||||
hotspot_x,
|
||||
hotspot_y,
|
||||
});
|
||||
let _ = surface_info.cursor_sender.send_replace(cursor_info);
|
||||
}
|
||||
wl_pointer::Request::Release => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlKeyboard, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &WlKeyboard,
|
||||
request: <WlKeyboard as Resource>::Request,
|
||||
_data: &Arc<SeatData>,
|
||||
_dh: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
wl_keyboard::Request::Release => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlTouch, Arc<SeatData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &WlTouch,
|
||||
request: <WlTouch as Resource>::Request,
|
||||
_data: &Arc<SeatData>,
|
||||
_dh: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
wl_touch::Request::Release => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,7 @@ use smithay::{
|
||||
input::{keyboard::XkbConfig, SeatState},
|
||||
output::{Mode, Output, Scale, Subpixel},
|
||||
reexports::{
|
||||
wayland_protocols::xdg::{
|
||||
decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
|
||||
shell::server::xdg_wm_base::XdgWmBase,
|
||||
},
|
||||
wayland_protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
|
||||
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
||||
wayland_server::{
|
||||
backend::{ClientData, ClientId, DisconnectReason},
|
||||
@@ -34,7 +31,7 @@ use smithay::{
|
||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||
},
|
||||
output::OutputHandler,
|
||||
shell::kde::decoration::KdeDecorationState,
|
||||
shell::{kde::decoration::KdeDecorationState, xdg::XdgShellState},
|
||||
shm::{ShmHandler, ShmState},
|
||||
},
|
||||
};
|
||||
@@ -43,6 +40,7 @@ use tokio::sync::mpsc::UnboundedSender;
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub struct ClientState {
|
||||
pub pid: Option<i32>,
|
||||
pub id: OnceCell<ClientId>,
|
||||
pub compositor_state: CompositorClientState,
|
||||
pub display: Weak<DisplayWrapper>,
|
||||
@@ -83,6 +81,7 @@ pub struct WaylandState {
|
||||
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||
pub seat_state: SeatState<Self>,
|
||||
pub seat: Arc<SeatWrapper>,
|
||||
pub xdg_shell: XdgShellState,
|
||||
pub output: Output,
|
||||
}
|
||||
|
||||
@@ -163,8 +162,9 @@ impl WaylandState {
|
||||
None,
|
||||
);
|
||||
output.set_preferred(mode);
|
||||
|
||||
let xdg_shell = XdgShellState::new::<Self>(&display_handle);
|
||||
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
|
||||
display_handle.create_global::<Self, XdgWmBase, _>(5, ());
|
||||
display_handle.create_global::<Self, ZxdgDecorationManagerV1, _>(1, ());
|
||||
display_handle.create_global::<Self, WlDrm, _>(2, ());
|
||||
|
||||
@@ -184,6 +184,7 @@ impl WaylandState {
|
||||
dmabuf_tx,
|
||||
seat_state,
|
||||
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
||||
xdg_shell,
|
||||
output,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -43,8 +43,8 @@ pub struct CoreSurface {
|
||||
sk_tex: OnceCell<Tex>,
|
||||
sk_mat: OnceCell<Arc<Material>>,
|
||||
material_offset: Mutex<Delta<u32>>,
|
||||
on_mapped: Box<dyn Fn() + Send + Sync>,
|
||||
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
||||
on_mapped: Mutex<Box<dyn Fn() + Send + Sync>>,
|
||||
on_commit: Mutex<Box<dyn Fn(u32) + Send + Sync>>,
|
||||
pub pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ impl CoreSurface {
|
||||
sk_tex: OnceCell::new(),
|
||||
sk_mat: OnceCell::new(),
|
||||
material_offset: Mutex::new(Delta::new(0)),
|
||||
on_mapped: Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>,
|
||||
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
||||
on_mapped: Mutex::new(Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>),
|
||||
on_commit: Mutex::new(Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>),
|
||||
pending_material_applications: Registry::new(),
|
||||
})
|
||||
});
|
||||
@@ -73,13 +73,18 @@ impl CoreSurface {
|
||||
}
|
||||
|
||||
pub fn commit(&self, count: u32) {
|
||||
(self.on_commit)(count);
|
||||
(*self.on_commit.lock())(count);
|
||||
}
|
||||
|
||||
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
|
||||
get_data(surf)
|
||||
}
|
||||
|
||||
pub fn decycle(&self) {
|
||||
*self.on_mapped.lock() = Box::new(|| {});
|
||||
*self.on_commit.lock() = Box::new(|_| {});
|
||||
}
|
||||
|
||||
pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) {
|
||||
let Some(wl_surface) = self.wl_surface() else {
|
||||
return;
|
||||
@@ -170,7 +175,7 @@ impl CoreSurface {
|
||||
});
|
||||
drop(mapped_data);
|
||||
if just_mapped {
|
||||
(self.on_mapped)();
|
||||
(*self.on_mapped.lock())();
|
||||
}
|
||||
self.apply_surface_materials();
|
||||
}
|
||||
|
||||
601
src/wayland/xdg_shell.rs
Normal file
601
src/wayland/xdg_shell.rs
Normal file
@@ -0,0 +1,601 @@
|
||||
use super::{
|
||||
seat::{handle_cursor, SeatWrapper},
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
utils,
|
||||
};
|
||||
use crate::nodes::{
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{
|
||||
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo,
|
||||
},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smithay::{
|
||||
delegate_xdg_shell,
|
||||
reexports::{
|
||||
wayland_protocols::xdg::{
|
||||
decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
||||
shell::server::xdg_toplevel::{ResizeEdge, State},
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
Resource,
|
||||
},
|
||||
},
|
||||
utils::{Logical, Rectangle, Serial},
|
||||
wayland::{
|
||||
compositor,
|
||||
shell::xdg::{
|
||||
Configure, PopupSurface, PositionerState, ShellClient, SurfaceCachedState,
|
||||
ToplevelSurface, XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
|
||||
},
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::warn;
|
||||
|
||||
impl From<Rectangle<i32, Logical>> for Geometry {
|
||||
fn from(value: Rectangle<i32, Logical>) -> Self {
|
||||
Geometry {
|
||||
origin: [value.loc.x, value.loc.y].into(),
|
||||
size: [value.size.w as u32, value.size.h as u32].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn surface_panel_item(wl_surface: &WlSurface) -> Option<Arc<PanelItem<XdgBackend>>> {
|
||||
let panel_item = utils::get_data::<Weak<PanelItem<XdgBackend>>>(wl_surface)
|
||||
.as_deref()
|
||||
.and_then(Weak::upgrade);
|
||||
if panel_item.is_none() {
|
||||
warn!("Couldn't get panel item");
|
||||
// println!("panel item not found at \n{}\n\n", {
|
||||
// let backtrace = Backtrace::force_capture().to_string();
|
||||
// let mut split_backtrace = backtrace
|
||||
// .split('\n')
|
||||
// .map(|s| s.to_string())
|
||||
// .collect::<Vec<_>>();
|
||||
// split_backtrace.resize(4, "".to_string());
|
||||
// split_backtrace.join("\n")
|
||||
// });
|
||||
}
|
||||
panel_item
|
||||
}
|
||||
|
||||
impl XdgShellHandler for WaylandState {
|
||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||
&mut self.xdg_shell
|
||||
}
|
||||
|
||||
fn new_client(&mut self, _client: ShellClient) {}
|
||||
fn client_destroyed(&mut self, _client: ShellClient) {}
|
||||
|
||||
fn new_toplevel(&mut self, toplevel: ToplevelSurface) {
|
||||
toplevel.with_pending_state(|s| {
|
||||
s.decoration_mode = Some(Mode::ClientSide);
|
||||
s.states.set(State::TiledTop);
|
||||
s.states.set(State::TiledBottom);
|
||||
s.states.set(State::TiledLeft);
|
||||
s.states.set(State::TiledRight);
|
||||
s.states.set(State::Maximized);
|
||||
s.states.unset(State::Fullscreen);
|
||||
});
|
||||
toplevel.send_configure();
|
||||
utils::insert_data(toplevel.wl_surface(), SurfaceID::Toplevel);
|
||||
CoreSurface::add_to(
|
||||
self.display_handle.clone(),
|
||||
toplevel.wl_surface(),
|
||||
{
|
||||
let toplevel = toplevel.clone();
|
||||
move || {
|
||||
let wl_surface = toplevel.wl_surface().client().unwrap();
|
||||
let client_state = wl_surface.get_data::<ClientState>().unwrap();
|
||||
let (node, panel_item) = PanelItem::create(
|
||||
Box::new(XdgBackend::create(
|
||||
toplevel.clone(),
|
||||
client_state.seat.clone(),
|
||||
)),
|
||||
client_state.pid.clone(),
|
||||
);
|
||||
handle_cursor(&panel_item, panel_item.backend.seat.cursor_info_rx.clone());
|
||||
utils::insert_data(toplevel.wl_surface(), Arc::downgrade(&panel_item));
|
||||
utils::insert_data_raw(toplevel.wl_surface(), node);
|
||||
}
|
||||
},
|
||||
{
|
||||
let toplevel = toplevel.clone();
|
||||
move |_c| {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
// if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
toplevel.send_configure();
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(size) = core_surface.size() else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_size_changed(size);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
fn toplevel_destroyed(&mut self, toplevel: ToplevelSurface) {
|
||||
if let Some(core_surface) = CoreSurface::from_wl_surface(toplevel.wl_surface()) {
|
||||
core_surface.decycle();
|
||||
}
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
panel_item.backend.seat.unfocus(toplevel.wl_surface(), self);
|
||||
panel_item.backend.toplevel.lock().take();
|
||||
panel_item.backend.popups.lock().clear();
|
||||
panel_item.drop_toplevel();
|
||||
// println!(
|
||||
// "Dropping toplevel resulted in {} references",
|
||||
// Arc::strong_count(&panel_item)
|
||||
// );
|
||||
}
|
||||
fn app_id_changed(&mut self, toplevel: ToplevelSurface) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.toplevel_app_id_changed(&compositor::with_states(
|
||||
toplevel.wl_surface(),
|
||||
|states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.app_id
|
||||
.clone()
|
||||
.unwrap()
|
||||
},
|
||||
))
|
||||
}
|
||||
fn title_changed(&mut self, toplevel: ToplevelSurface) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.toplevel_title_changed(&compositor::with_states(
|
||||
toplevel.wl_surface(),
|
||||
|states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.title
|
||||
.clone()
|
||||
.unwrap()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn new_popup(&mut self, popup: PopupSurface, positioner: PositionerState) {
|
||||
let uid = nanoid::nanoid!();
|
||||
let Some(parent) = popup.get_parent_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = surface_panel_item(&parent) else {
|
||||
return;
|
||||
};
|
||||
if popup.send_configure().is_err() {
|
||||
return;
|
||||
}
|
||||
utils::insert_data(popup.wl_surface(), SurfaceID::Child(uid.clone()));
|
||||
utils::insert_data(popup.wl_surface(), uid.clone());
|
||||
utils::insert_data(popup.wl_surface(), Arc::downgrade(&panel_item));
|
||||
CoreSurface::add_to(
|
||||
self.display_handle.clone(),
|
||||
popup.wl_surface(),
|
||||
{
|
||||
let popup = popup.clone();
|
||||
move || {
|
||||
panel_item
|
||||
.backend
|
||||
.new_popup(&uid, popup.clone(), positioner);
|
||||
}
|
||||
},
|
||||
{
|
||||
let popup = popup.clone();
|
||||
move |_c| {
|
||||
if surface_panel_item(popup.wl_surface()).is_none() {
|
||||
// if the popup toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
let _ = popup.send_configure();
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
fn reposition_request(
|
||||
&mut self,
|
||||
popup: PopupSurface,
|
||||
positioner: PositionerState,
|
||||
_token: u32,
|
||||
) {
|
||||
let Some(panel_item) = surface_panel_item(popup.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
let Some(uid) = utils::get_data::<String>(popup.wl_surface())
|
||||
.as_deref()
|
||||
.cloned()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
panel_item.backend.reposition_popup(&uid, popup, positioner)
|
||||
}
|
||||
fn popup_destroyed(&mut self, popup: PopupSurface) {
|
||||
if let Some(core_surface) = CoreSurface::from_wl_surface(popup.wl_surface()) {
|
||||
core_surface.decycle();
|
||||
}
|
||||
let Some(panel_item) = surface_panel_item(popup.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
let Some(uid) = utils::get_data::<String>(popup.wl_surface())
|
||||
.as_deref()
|
||||
.cloned()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
panel_item.backend.seat.unfocus(popup.wl_surface(), self);
|
||||
panel_item.backend.drop_popup(&uid);
|
||||
}
|
||||
|
||||
fn grab(&mut self, _popup: PopupSurface, _seat: WlSeat, _serial: Serial) {}
|
||||
|
||||
fn move_request(&mut self, toplevel: ToplevelSurface, _seat: WlSeat, _serial: Serial) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_move_request();
|
||||
}
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
toplevel: ToplevelSurface,
|
||||
_seat: WlSeat,
|
||||
_serial: Serial,
|
||||
edges: ResizeEdge,
|
||||
) {
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
let (up, down, left, right) = match edges {
|
||||
ResizeEdge::None => (false, false, false, false),
|
||||
ResizeEdge::Top => (true, false, false, false),
|
||||
ResizeEdge::Bottom => (false, true, false, false),
|
||||
ResizeEdge::Left => (false, false, true, false),
|
||||
ResizeEdge::TopLeft => (true, false, true, false),
|
||||
ResizeEdge::BottomLeft => (false, true, true, false),
|
||||
ResizeEdge::Right => (false, false, false, true),
|
||||
ResizeEdge::TopRight => (true, false, false, true),
|
||||
ResizeEdge::BottomRight => (false, true, false, true),
|
||||
_ => (false, false, false, false),
|
||||
};
|
||||
panel_item.toplevel_resize_request(up, down, left, right);
|
||||
}
|
||||
|
||||
fn maximize_request(&mut self, toplevel: ToplevelSurface) {
|
||||
toplevel.with_pending_state(|s| {
|
||||
s.states.set(State::Maximized);
|
||||
s.states.unset(State::Fullscreen);
|
||||
});
|
||||
toplevel.send_configure();
|
||||
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_fullscreen_active(false);
|
||||
}
|
||||
fn fullscreen_request(&mut self, toplevel: ToplevelSurface, _output: Option<WlOutput>) {
|
||||
toplevel.with_pending_state(|s| {
|
||||
s.states.set(State::Fullscreen);
|
||||
s.states.unset(State::Maximized);
|
||||
});
|
||||
toplevel.send_configure();
|
||||
|
||||
let Some(panel_item) = surface_panel_item(toplevel.wl_surface()) else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_fullscreen_active(true);
|
||||
}
|
||||
|
||||
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
|
||||
let Some(panel_item) = surface_panel_item(&surface) else {
|
||||
return;
|
||||
};
|
||||
match configure {
|
||||
Configure::Toplevel(t) => {
|
||||
if let Some(size) = t.state.size {
|
||||
panel_item.toplevel_size_changed([size.w as u32, size.h as u32].into())
|
||||
}
|
||||
}
|
||||
Configure::Popup(_p) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate_xdg_shell!(WaylandState);
|
||||
|
||||
pub struct XdgBackend {
|
||||
toplevel: Mutex<Option<ToplevelSurface>>,
|
||||
popups: Mutex<FxHashMap<String, (PopupSurface, PositionerState)>>,
|
||||
pub seat: Arc<SeatWrapper>,
|
||||
}
|
||||
impl XdgBackend {
|
||||
pub fn create(toplevel: ToplevelSurface, seat: Arc<SeatWrapper>) -> Self {
|
||||
XdgBackend {
|
||||
toplevel: Mutex::new(Some(toplevel)),
|
||||
popups: Mutex::new(FxHashMap::default()),
|
||||
seat,
|
||||
}
|
||||
}
|
||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||
match id {
|
||||
SurfaceID::Cursor => self
|
||||
.seat
|
||||
.cursor_info_rx
|
||||
.borrow()
|
||||
.surface
|
||||
.clone()?
|
||||
.upgrade()
|
||||
.ok(),
|
||||
SurfaceID::Toplevel => Some(self.toplevel.lock().clone()?.wl_surface().clone()),
|
||||
SurfaceID::Child(popup) => {
|
||||
let popups = self.popups.lock();
|
||||
Some(popups.get(popup)?.0.wl_surface().clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn panel_item(&self) -> Option<Arc<PanelItem<XdgBackend>>> {
|
||||
surface_panel_item(self.toplevel.lock().clone()?.wl_surface())
|
||||
}
|
||||
|
||||
pub fn new_popup(&self, uid: &str, popup: PopupSurface, positioner: PositionerState) {
|
||||
let Some(panel_item) = self.panel_item() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.popups
|
||||
.lock()
|
||||
.insert(uid.to_string(), (popup, positioner));
|
||||
|
||||
panel_item.new_child(uid, self.child_data(uid).unwrap());
|
||||
}
|
||||
pub fn reposition_popup(&self, uid: &str, _popup: PopupSurface, positioner: PositionerState) {
|
||||
self.popups.lock().get_mut(uid).unwrap().1 = positioner;
|
||||
|
||||
let Some(panel_item) = self.panel_item() else {
|
||||
return;
|
||||
};
|
||||
let geometry = positioner.get_geometry();
|
||||
|
||||
panel_item.reposition_child(uid, geometry.into());
|
||||
}
|
||||
pub fn drop_popup(&self, uid: &str) {
|
||||
let Some(panel_item) = self.panel_item() else {
|
||||
return;
|
||||
};
|
||||
panel_item.drop_child(uid);
|
||||
}
|
||||
|
||||
fn child_data(&self, uid: &str) -> Option<ChildInfo> {
|
||||
let (popup, positioner) = self.popups.lock().get(uid)?.clone();
|
||||
Some(ChildInfo {
|
||||
parent: (*utils::get_data::<SurfaceID>(&popup.get_parent_surface()?)?).clone(),
|
||||
geometry: positioner.get_geometry().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Backend for XdgBackend {
|
||||
fn start_data(&self) -> Result<PanelItemInitData> {
|
||||
let cursor = self
|
||||
.seat
|
||||
.cursor_info_rx
|
||||
.borrow()
|
||||
.surface
|
||||
.clone()
|
||||
.and_then(|s| s.upgrade().ok())
|
||||
.as_ref()
|
||||
.and_then(CoreSurface::from_wl_surface)
|
||||
.and_then(|c| c.size())
|
||||
.map(|size| Geometry {
|
||||
origin: [0; 2].into(),
|
||||
size,
|
||||
});
|
||||
|
||||
let toplevel = self.toplevel.lock().clone().unwrap();
|
||||
let app_id = compositor::with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.title
|
||||
.clone()
|
||||
});
|
||||
let title = compositor::with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.app_id
|
||||
.clone()
|
||||
});
|
||||
let toplevel_cached_state = compositor::with_states(toplevel.wl_surface(), |states| {
|
||||
states.cached_state.current::<SurfaceCachedState>().clone()
|
||||
});
|
||||
let toplevel_core_surface = CoreSurface::from_wl_surface(toplevel.wl_surface()).unwrap();
|
||||
|
||||
let size = toplevel
|
||||
.current_state()
|
||||
.size
|
||||
.clone()
|
||||
.map(|s| [s.w as u32, s.h as u32].into())
|
||||
.or_else(|| toplevel_core_surface.size())
|
||||
.unwrap_or([0; 2].into());
|
||||
let toplevel = ToplevelInfo {
|
||||
parent: toplevel
|
||||
.parent()
|
||||
.as_ref()
|
||||
.and_then(surface_panel_item)
|
||||
.map(|p| p.uid.clone()),
|
||||
title,
|
||||
app_id,
|
||||
size,
|
||||
min_size: if toplevel_cached_state.min_size.w != 0
|
||||
&& toplevel_cached_state.min_size.h != 0
|
||||
{
|
||||
Some(
|
||||
[
|
||||
toplevel_cached_state.min_size.w as u32,
|
||||
toplevel_cached_state.min_size.h as u32,
|
||||
]
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
max_size: if toplevel_cached_state.max_size.w != 0
|
||||
&& toplevel_cached_state.max_size.h != 0
|
||||
{
|
||||
Some(
|
||||
[
|
||||
toplevel_cached_state.max_size.w as u32,
|
||||
toplevel_cached_state.max_size.h as u32,
|
||||
]
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
logical_rectangle: toplevel_cached_state
|
||||
.geometry
|
||||
.map(Into::into)
|
||||
.unwrap_or(Geometry {
|
||||
origin: [0; 2].into(),
|
||||
size,
|
||||
}),
|
||||
};
|
||||
|
||||
let children = self
|
||||
.popups
|
||||
.lock()
|
||||
.keys()
|
||||
.map(|k| (k.clone(), self.child_data(k).unwrap()))
|
||||
.collect();
|
||||
|
||||
Ok(PanelItemInitData {
|
||||
cursor,
|
||||
toplevel,
|
||||
children,
|
||||
pointer_grab: None,
|
||||
keyboard_grab: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn surface_alive(&self, surface: &SurfaceID) -> bool {
|
||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||
return false;
|
||||
};
|
||||
surface.is_alive()
|
||||
}
|
||||
|
||||
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||
let Some(surface) = self.wl_surface_from_id(&surface) else {
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&surface) else {
|
||||
return;
|
||||
};
|
||||
core_surface.apply_material(model_part);
|
||||
}
|
||||
|
||||
fn close_toplevel(&self) {
|
||||
if let Some(toplevel) = self.toplevel.lock().clone() {
|
||||
toplevel.send_close();
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_size_toplevel(&self) {
|
||||
let Some(toplevel) = self.toplevel.lock().clone() else {
|
||||
return;
|
||||
};
|
||||
toplevel.with_pending_state(|s| s.size = None);
|
||||
toplevel.send_configure();
|
||||
}
|
||||
fn set_toplevel_size(&self, size: Vector2<u32>) {
|
||||
let Some(toplevel) = self.toplevel.lock().clone() else {
|
||||
return;
|
||||
};
|
||||
toplevel.with_pending_state(|s| s.size = Some((size.x as i32, size.y as i32).into()));
|
||||
toplevel.send_configure();
|
||||
}
|
||||
fn set_toplevel_focused_visuals(&self, focused: bool) {
|
||||
let Some(toplevel) = self.toplevel.lock().clone() else {
|
||||
return;
|
||||
};
|
||||
toplevel.with_pending_state(|s| {
|
||||
if focused {
|
||||
s.states.set(State::Activated);
|
||||
} else {
|
||||
s.states.unset(State::Activated);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(&surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.pointer_motion(surface, position)
|
||||
}
|
||||
fn pointer_button(&self, _surface: &SurfaceID, button: u32, pressed: bool) {
|
||||
self.seat.pointer_button(button, pressed)
|
||||
}
|
||||
fn pointer_scroll(
|
||||
&self,
|
||||
_surface: &SurfaceID,
|
||||
scroll_distance: Option<Vector2<f32>>,
|
||||
scroll_steps: Option<Vector2<f32>>,
|
||||
) {
|
||||
self.seat.pointer_scroll(scroll_distance, scroll_steps)
|
||||
}
|
||||
|
||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(&surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.keyboard_keys(surface, keymap_id, keys)
|
||||
}
|
||||
|
||||
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(&surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.touch_down(surface, id, position)
|
||||
}
|
||||
fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
||||
self.seat.touch_move(id, position)
|
||||
}
|
||||
fn touch_up(&self, id: u32) {
|
||||
self.seat.touch_up(id)
|
||||
}
|
||||
fn reset_touches(&self) {
|
||||
self.seat.reset_touches()
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
use super::{popup::PopupData, surface::XdgSurfaceData, ToplevelData};
|
||||
use crate::{
|
||||
nodes::{
|
||||
drawable::model::ModelPart,
|
||||
items::panel::{Backend, ChildInfo, PanelItem, PanelItemInitData, SurfaceID},
|
||||
},
|
||||
wayland::{seat::SeatWrapper, state::ClientState, surface::CoreSurface, utils, SERIAL_COUNTER},
|
||||
};
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
|
||||
wayland_server::{protocol::wl_surface::WlSurface, Resource, Weak as WlWeak},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
|
||||
pub struct XdgToplevelState {
|
||||
pub fullscreen: bool,
|
||||
pub activated: bool,
|
||||
}
|
||||
|
||||
pub struct XdgBackend {
|
||||
toplevel: WlWeak<XdgToplevel>,
|
||||
toplevel_wl_surface: WlWeak<WlSurface>,
|
||||
pub toplevel_state: Mutex<XdgToplevelState>,
|
||||
popups: Mutex<FxHashMap<String, WlWeak<WlSurface>>>,
|
||||
pub seat: Arc<SeatWrapper>,
|
||||
}
|
||||
impl XdgBackend {
|
||||
pub fn create(
|
||||
toplevel_wl_surface: WlSurface,
|
||||
toplevel: XdgToplevel,
|
||||
seat: Arc<SeatWrapper>,
|
||||
) -> Self {
|
||||
XdgBackend {
|
||||
toplevel: toplevel.downgrade(),
|
||||
toplevel_wl_surface: toplevel_wl_surface.downgrade(),
|
||||
toplevel_state: Mutex::new(XdgToplevelState {
|
||||
fullscreen: false,
|
||||
activated: false,
|
||||
}),
|
||||
popups: Mutex::new(FxHashMap::default()),
|
||||
seat,
|
||||
}
|
||||
}
|
||||
fn wl_surface_from_id(&self, id: &SurfaceID) -> Option<WlSurface> {
|
||||
match id {
|
||||
SurfaceID::Cursor => self
|
||||
.seat
|
||||
.cursor_info_rx
|
||||
.borrow()
|
||||
.surface
|
||||
.clone()?
|
||||
.upgrade()
|
||||
.ok(),
|
||||
SurfaceID::Toplevel => self.toplevel_wl_surface(),
|
||||
SurfaceID::Child(popup) => {
|
||||
let popups = self.popups.lock();
|
||||
popups.get(popup)?.upgrade().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn toplevel_wl_surface(&self) -> Option<WlSurface> {
|
||||
self.toplevel_wl_surface.upgrade().ok()
|
||||
}
|
||||
|
||||
pub fn configure(&self, size: Option<Vector2<u32>>) {
|
||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(wl_surface) = self.toplevel_wl_surface() else {
|
||||
return;
|
||||
};
|
||||
let Some(xdg_surface_data) = wl_surface.data::<XdgSurfaceData>() else {
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {
|
||||
return;
|
||||
};
|
||||
let Some(surface_size) = core_surface.size() else {
|
||||
return;
|
||||
};
|
||||
|
||||
xdg_toplevel.configure(
|
||||
size.unwrap_or(surface_size).x as i32,
|
||||
size.unwrap_or(surface_size).y as i32,
|
||||
self.states()
|
||||
.into_iter()
|
||||
.flat_map(|state| state.to_ne_bytes())
|
||||
.collect(),
|
||||
);
|
||||
xdg_surface_data.xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
self.flush_client();
|
||||
}
|
||||
fn states(&self) -> Vec<u32> {
|
||||
let mut states = vec![1, 5, 6, 7, 8]; // maximized always and tiled
|
||||
let toplevel_state = self.toplevel_state.lock();
|
||||
if toplevel_state.fullscreen {
|
||||
states.push(2);
|
||||
}
|
||||
if toplevel_state.activated {
|
||||
states.push(4);
|
||||
}
|
||||
states
|
||||
}
|
||||
|
||||
pub fn new_popup(
|
||||
&self,
|
||||
panel_item: &PanelItem<XdgBackend>,
|
||||
popup_wl_surface: &WlSurface,
|
||||
data: &PopupData,
|
||||
) {
|
||||
self.popups
|
||||
.lock()
|
||||
.insert(data.uid.clone(), popup_wl_surface.downgrade());
|
||||
|
||||
let Some(geometry) = data.geometry() else {
|
||||
return;
|
||||
};
|
||||
panel_item.new_child(
|
||||
&data.uid,
|
||||
ChildInfo {
|
||||
parent: utils::get_data::<SurfaceID>(&data.parent())
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.clone(),
|
||||
geometry,
|
||||
},
|
||||
)
|
||||
}
|
||||
pub fn reposition_popup(&self, panel_item: &PanelItem<XdgBackend>, popup_state: &PopupData) {
|
||||
let Some(geometry) = popup_state.geometry() else {
|
||||
return;
|
||||
};
|
||||
panel_item.reposition_child(&popup_state.uid, geometry)
|
||||
}
|
||||
pub fn drop_popup(&self, panel_item: &PanelItem<XdgBackend>, uid: &str) {
|
||||
panel_item.drop_child(uid);
|
||||
}
|
||||
|
||||
fn child_data(&self) -> FxHashMap<String, ChildInfo> {
|
||||
FxHashMap::from_iter(self.popups.lock().iter().filter_map(|(uid, v)| {
|
||||
let wl_surface = v.upgrade().ok()?;
|
||||
let popup_data = utils::get_data::<PopupData>(&wl_surface)?;
|
||||
let parent = utils::get_data::<SurfaceID>(&popup_data.parent())?
|
||||
.as_ref()
|
||||
.clone();
|
||||
let geometry = utils::get_data::<XdgSurfaceData>(&wl_surface)?
|
||||
.geometry
|
||||
.lock()
|
||||
.clone()?;
|
||||
Some((uid.clone(), ChildInfo { parent, geometry }))
|
||||
}))
|
||||
}
|
||||
|
||||
fn flush_client(&self) {
|
||||
let Some(client) = self.toplevel_wl_surface().and_then(|s| s.client()) else {
|
||||
return;
|
||||
};
|
||||
if let Some(client_state) = client.get_data::<ClientState>() {
|
||||
client_state.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for XdgBackend {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropped panel item gracefully");
|
||||
}
|
||||
}
|
||||
impl Backend for XdgBackend {
|
||||
fn start_data(&self) -> Result<PanelItemInitData> {
|
||||
let toplevel = self.toplevel_wl_surface();
|
||||
let toplevel_data = toplevel.as_ref().and_then(utils::get_data::<ToplevelData>);
|
||||
let toplevel_data = toplevel_data
|
||||
.as_deref()
|
||||
.clone()
|
||||
.ok_or_else(|| eyre!("Could not get toplevel"))?;
|
||||
|
||||
Ok(PanelItemInitData {
|
||||
cursor: (*self.seat.cursor_info_rx.borrow()).cursor_data(),
|
||||
toplevel: toplevel_data.into(),
|
||||
children: self.child_data(),
|
||||
pointer_grab: None,
|
||||
keyboard_grab: None,
|
||||
})
|
||||
}
|
||||
fn surface_alive(&self, surface: &SurfaceID) -> bool {
|
||||
match surface {
|
||||
SurfaceID::Cursor => true,
|
||||
SurfaceID::Toplevel => true,
|
||||
SurfaceID::Child(c) => self.popups.lock().contains_key(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
|
||||
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
core_surface.apply_material(model_part);
|
||||
}
|
||||
|
||||
fn close_toplevel(&self) {
|
||||
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {
|
||||
return;
|
||||
};
|
||||
xdg_toplevel.close();
|
||||
}
|
||||
fn auto_size_toplevel(&self) {
|
||||
self.configure(Some([0, 0].into()));
|
||||
}
|
||||
fn set_toplevel_size(&self, size: Vector2<u32>) {
|
||||
self.configure(Some(size));
|
||||
}
|
||||
fn set_toplevel_focused_visuals(&self, focused: bool) {
|
||||
self.toplevel_state.lock().activated = focused;
|
||||
self.configure(None);
|
||||
}
|
||||
|
||||
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.pointer_motion(surface, position)
|
||||
}
|
||||
fn pointer_button(&self, _surface: &SurfaceID, button: u32, pressed: bool) {
|
||||
self.seat.pointer_button(button, pressed)
|
||||
}
|
||||
fn pointer_scroll(
|
||||
&self,
|
||||
_surface: &SurfaceID,
|
||||
scroll_distance: Option<Vector2<f32>>,
|
||||
scroll_steps: Option<Vector2<f32>>,
|
||||
) {
|
||||
self.seat.pointer_scroll(scroll_distance, scroll_steps)
|
||||
}
|
||||
|
||||
fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec<i32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.keyboard_keys(surface, keymap_id, keys)
|
||||
}
|
||||
|
||||
fn touch_down(&self, surface: &SurfaceID, id: u32, position: Vector2<f32>) {
|
||||
let Some(surface) = self.wl_surface_from_id(surface) else {
|
||||
return;
|
||||
};
|
||||
self.seat.touch_down(surface, id, position)
|
||||
}
|
||||
fn touch_move(&self, id: u32, position: Vector2<f32>) {
|
||||
self.seat.touch_move(id, position)
|
||||
}
|
||||
fn touch_up(&self, id: u32) {
|
||||
self.seat.touch_up(id)
|
||||
}
|
||||
fn reset_touches(&self) {
|
||||
self.seat.reset_touches()
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
use self::{backend::XdgBackend, toplevel::ToplevelData};
|
||||
use super::state::WaylandState;
|
||||
use crate::wayland::{
|
||||
utils::insert_data,
|
||||
xdg_shell::{positioner::PositionerData, surface::XdgSurfaceData},
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_wm_base::{self, XdgWmBase},
|
||||
wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
mod backend;
|
||||
mod popup;
|
||||
mod positioner;
|
||||
mod surface;
|
||||
mod toplevel;
|
||||
|
||||
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
fn bind(
|
||||
_state: &mut WaylandState,
|
||||
_handle: &DisplayHandle,
|
||||
_client: &Client,
|
||||
resource: New<XdgWmBase>,
|
||||
_global_data: &(),
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
data_init.init(resource, ());
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgWmBase, (), WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
_resource: &XdgWmBase,
|
||||
request: xdg_wm_base::Request,
|
||||
_data: &(),
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_wm_base::Request::CreatePositioner { id } => {
|
||||
let positioner = data_init.init(id, Mutex::new(PositionerData::default()));
|
||||
debug!(?positioner, "Create XDG positioner");
|
||||
}
|
||||
xdg_wm_base::Request::GetXdgSurface { id, surface } => {
|
||||
let xdg_surface = data_init.init(id, surface.downgrade());
|
||||
debug!(?xdg_surface, "Create XDG surface");
|
||||
insert_data(
|
||||
&surface,
|
||||
XdgSurfaceData {
|
||||
wl_surface: surface.downgrade(),
|
||||
xdg_surface,
|
||||
geometry: Mutex::new(None),
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_wm_base::Request::Pong { serial } => {
|
||||
debug!(serial, "Client pong");
|
||||
}
|
||||
xdg_wm_base::Request::Destroy => {
|
||||
debug!("Destroy XDG WM base");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
use super::{backend::XdgBackend, positioner::PositionerData};
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, SurfaceID},
|
||||
wayland::{state::WaylandState, utils::get_data},
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_popup::{self, XdgPopup},
|
||||
xdg_positioner::XdgPositioner,
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{debug, error};
|
||||
use wayland_backend::server::ClientId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PopupData {
|
||||
pub uid: String,
|
||||
grabbed: Mutex<bool>,
|
||||
parent: Mutex<WlWeak<WlSurface>>,
|
||||
panel_item: Weak<PanelItem<XdgBackend>>,
|
||||
positioner: Mutex<XdgPositioner>,
|
||||
}
|
||||
impl PopupData {
|
||||
pub fn new(
|
||||
uid: impl ToString,
|
||||
parent: WlSurface,
|
||||
panel_item: &Arc<PanelItem<XdgBackend>>,
|
||||
positioner: XdgPositioner,
|
||||
) -> Self {
|
||||
PopupData {
|
||||
uid: uid.to_string(),
|
||||
grabbed: Mutex::new(false),
|
||||
parent: Mutex::new(parent.downgrade()),
|
||||
panel_item: Arc::downgrade(panel_item),
|
||||
positioner: Mutex::new(positioner),
|
||||
}
|
||||
}
|
||||
pub fn geometry(&self) -> Option<Geometry> {
|
||||
let positioner = self.positioner.lock().clone();
|
||||
let positioner_data = positioner.data::<Mutex<PositionerData>>()?.lock();
|
||||
Some(positioner_data.clone().into())
|
||||
}
|
||||
pub fn parent(&self) -> WlSurface {
|
||||
self.parent.lock().upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPopup, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_popup: &XdgPopup,
|
||||
request: xdg_popup::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg popup");
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = get_data::<PopupData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgPopup");
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = popup_data.panel_item.upgrade() else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_popup::Request::Grab { seat, serial } => {
|
||||
*popup_data.grabbed.lock() = true;
|
||||
debug!(?xdg_popup, ?seat, serial, "XDG popup grab");
|
||||
panel_item.grab_keyboard(Some(SurfaceID::Child(popup_data.uid.clone())));
|
||||
}
|
||||
xdg_popup::Request::Reposition { positioner, token } => {
|
||||
debug!(?xdg_popup, ?positioner, token, "XDG popup reposition");
|
||||
*popup_data.positioner.lock() = positioner;
|
||||
panel_item
|
||||
.backend
|
||||
.reposition_popup(&panel_item, &popup_data);
|
||||
}
|
||||
xdg_popup::Request::Destroy => {
|
||||
debug!(?xdg_popup, "Destroy XDG popup");
|
||||
if *popup_data.grabbed.lock() {
|
||||
panel_item.grab_keyboard(None);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyed(
|
||||
_state: &mut WaylandState,
|
||||
_client: ClientId,
|
||||
_popup: &XdgPopup,
|
||||
data: &WlWeak<WlSurface>,
|
||||
) {
|
||||
let Ok(wl_surface) = data.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg popup");
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = get_data::<PopupData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgPopup");
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = popup_data.panel_item.upgrade() else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.drop_popup(&panel_item, &popup_data.uid);
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
use crate::{nodes::items::panel::Geometry, wayland::state::WaylandState};
|
||||
use mint::Vector2;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_positioner::{
|
||||
self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner,
|
||||
},
|
||||
wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource},
|
||||
};
|
||||
use tracing::{debug, warn};
|
||||
use wayland_backend::protocol::WEnum;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PositionerData {
|
||||
size: Vector2<u32>,
|
||||
anchor_rect_pos: Vector2<i32>,
|
||||
anchor_rect_size: Vector2<u32>,
|
||||
anchor: Anchor,
|
||||
gravity: Gravity,
|
||||
constraint_adjustment: ConstraintAdjustment,
|
||||
offset: Vector2<i32>,
|
||||
reactive: bool,
|
||||
}
|
||||
impl Default for PositionerData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: Vector2::from([0; 2]),
|
||||
anchor_rect_pos: Vector2::from([0; 2]),
|
||||
anchor_rect_size: Vector2::from([0; 2]),
|
||||
anchor: Anchor::None,
|
||||
gravity: Gravity::None,
|
||||
constraint_adjustment: ConstraintAdjustment::None,
|
||||
offset: Vector2::from([0; 2]),
|
||||
reactive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PositionerData {
|
||||
fn anchor_has_edge(&self, edge: Anchor) -> bool {
|
||||
match edge {
|
||||
Anchor::Top => {
|
||||
self.anchor == Anchor::Top
|
||||
|| self.anchor == Anchor::TopLeft
|
||||
|| self.anchor == Anchor::TopRight
|
||||
}
|
||||
Anchor::Bottom => {
|
||||
self.anchor == Anchor::Bottom
|
||||
|| self.anchor == Anchor::BottomLeft
|
||||
|| self.anchor == Anchor::BottomRight
|
||||
}
|
||||
Anchor::Left => {
|
||||
self.anchor == Anchor::Left
|
||||
|| self.anchor == Anchor::TopLeft
|
||||
|| self.anchor == Anchor::BottomLeft
|
||||
}
|
||||
Anchor::Right => {
|
||||
self.anchor == Anchor::Right
|
||||
|| self.anchor == Anchor::TopRight
|
||||
|| self.anchor == Anchor::BottomRight
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gravity_has_edge(&self, edge: Gravity) -> bool {
|
||||
match edge {
|
||||
Gravity::Top => {
|
||||
self.gravity == Gravity::Top
|
||||
|| self.gravity == Gravity::TopLeft
|
||||
|| self.gravity == Gravity::TopRight
|
||||
}
|
||||
Gravity::Bottom => {
|
||||
self.gravity == Gravity::Bottom
|
||||
|| self.gravity == Gravity::BottomLeft
|
||||
|| self.gravity == Gravity::BottomRight
|
||||
}
|
||||
Gravity::Left => {
|
||||
self.gravity == Gravity::Left
|
||||
|| self.gravity == Gravity::TopLeft
|
||||
|| self.gravity == Gravity::BottomLeft
|
||||
}
|
||||
Gravity::Right => {
|
||||
self.gravity == Gravity::Right
|
||||
|| self.gravity == Gravity::TopRight
|
||||
|| self.gravity == Gravity::BottomRight
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pos(&self) -> Vector2<i32> {
|
||||
let mut pos = self.offset;
|
||||
|
||||
if self.anchor_has_edge(Anchor::Top) {
|
||||
pos.y += self.anchor_rect_pos.y;
|
||||
} else if self.anchor_has_edge(Anchor::Bottom) {
|
||||
pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32;
|
||||
} else {
|
||||
pos.y += self.anchor_rect_pos.y + self.anchor_rect_size.y as i32 / 2;
|
||||
}
|
||||
|
||||
if self.anchor_has_edge(Anchor::Left) {
|
||||
pos.x += self.anchor_rect_pos.x;
|
||||
} else if self.anchor_has_edge(Anchor::Right) {
|
||||
pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32;
|
||||
} else {
|
||||
pos.x += self.anchor_rect_pos.x + self.anchor_rect_size.x as i32 / 2;
|
||||
}
|
||||
|
||||
if self.gravity_has_edge(Gravity::Top) {
|
||||
pos.y -= self.size.y as i32;
|
||||
} else if !self.gravity_has_edge(Gravity::Bottom) {
|
||||
pos.y -= self.size.y as i32 / 2;
|
||||
}
|
||||
|
||||
if self.gravity_has_edge(Gravity::Left) {
|
||||
pos.x -= self.size.x as i32;
|
||||
} else if !self.gravity_has_edge(Gravity::Right) {
|
||||
pos.x -= self.size.x as i32 / 2;
|
||||
}
|
||||
|
||||
pos
|
||||
}
|
||||
}
|
||||
impl From<PositionerData> for Geometry {
|
||||
fn from(value: PositionerData) -> Self {
|
||||
Geometry {
|
||||
origin: value.get_pos(),
|
||||
size: value.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgPositioner, Mutex<PositionerData>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
_state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
positioner: &XdgPositioner,
|
||||
request: xdg_positioner::Request,
|
||||
data: &Mutex<PositionerData>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
match request {
|
||||
xdg_positioner::Request::SetSize { width, height } => {
|
||||
debug!(?positioner, width, height, "Set positioner size");
|
||||
data.lock().size = Vector2::from([width as u32, height as u32]);
|
||||
}
|
||||
xdg_positioner::Request::SetAnchorRect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
if width < 1 || height < 1 {
|
||||
positioner.post_error(
|
||||
xdg_positioner::Error::InvalidInput,
|
||||
"Invalid size for positioner's anchor rectangle.",
|
||||
);
|
||||
warn!(
|
||||
?positioner,
|
||||
width, height, "Invalid size for positioner's anchor rectangle"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
?positioner,
|
||||
x, y, width, height, "Set positioner anchor rectangle"
|
||||
);
|
||||
let mut data = data.lock();
|
||||
data.anchor_rect_pos = [x, y].into();
|
||||
data.anchor_rect_size = [width as u32, height as u32].into();
|
||||
}
|
||||
xdg_positioner::Request::SetAnchor { anchor } => {
|
||||
if let WEnum::Value(anchor) = anchor {
|
||||
debug!(?positioner, ?anchor, "Set positioner anchor");
|
||||
data.lock().anchor = anchor;
|
||||
}
|
||||
}
|
||||
xdg_positioner::Request::SetGravity { gravity } => {
|
||||
if let WEnum::Value(gravity) = gravity {
|
||||
debug!(?positioner, ?gravity, "Set positioner gravity");
|
||||
data.lock().gravity = gravity;
|
||||
}
|
||||
}
|
||||
xdg_positioner::Request::SetConstraintAdjustment {
|
||||
constraint_adjustment,
|
||||
} => {
|
||||
debug!(
|
||||
?positioner,
|
||||
constraint_adjustment, "Set positioner constraint adjustment"
|
||||
);
|
||||
let Some(constraint_adjustment) =
|
||||
ConstraintAdjustment::from_bits(constraint_adjustment)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
data.lock().constraint_adjustment = constraint_adjustment;
|
||||
}
|
||||
xdg_positioner::Request::SetOffset { x, y } => {
|
||||
debug!(?positioner, x, y, "Set positioner offset");
|
||||
data.lock().offset = [x, y].into();
|
||||
}
|
||||
xdg_positioner::Request::SetReactive => {
|
||||
debug!(?positioner, "Set positioner reactive");
|
||||
data.lock().reactive = true;
|
||||
}
|
||||
xdg_positioner::Request::SetParentSize {
|
||||
parent_width,
|
||||
parent_height,
|
||||
} => {
|
||||
debug!(
|
||||
?positioner,
|
||||
parent_width, parent_height, "Set positioner parent size"
|
||||
);
|
||||
}
|
||||
xdg_positioner::Request::SetParentConfigure { serial } => {
|
||||
debug!(?positioner, serial, "Set positioner parent size");
|
||||
}
|
||||
xdg_positioner::Request::Destroy => (),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, SurfaceID},
|
||||
wayland::{
|
||||
seat::handle_cursor,
|
||||
state::{ClientState, WaylandState},
|
||||
surface::CoreSurface,
|
||||
utils,
|
||||
xdg_shell::{popup::PopupData, toplevel::ToplevelData, XdgBackend},
|
||||
SERIAL_COUNTER,
|
||||
},
|
||||
};
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::{
|
||||
xdg_surface::{self, XdgSurface},
|
||||
xdg_toplevel::{WmCapabilities, XdgToplevel, EVT_WM_CAPABILITIES_SINCE},
|
||||
},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{debug, error};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XdgSurfaceData {
|
||||
pub wl_surface: WlWeak<WlSurface>,
|
||||
pub xdg_surface: XdgSurface,
|
||||
pub geometry: Mutex<Option<Geometry>>,
|
||||
}
|
||||
|
||||
impl Dispatch<XdgSurface, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
state: &mut WaylandState,
|
||||
client: &Client,
|
||||
xdg_surface: &XdgSurface,
|
||||
request: xdg_surface::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg surface");
|
||||
return;
|
||||
};
|
||||
let Some(xdg_surface_data) = utils::get_data::<XdgSurfaceData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgSurface");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_surface::Request::GetToplevel { id } => {
|
||||
let toplevel = data_init.init(id, wl_surface_resource.clone());
|
||||
utils::insert_data(&wl_surface, SurfaceID::Toplevel);
|
||||
utils::insert_data(&wl_surface, toplevel.clone());
|
||||
utils::insert_data(&wl_surface, ToplevelData::new(&wl_surface));
|
||||
debug!(?toplevel, ?xdg_surface, "Create XDG toplevel");
|
||||
|
||||
if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE {
|
||||
toplevel.wm_capabilities(
|
||||
vec![WmCapabilities::Maximize, WmCapabilities::Fullscreen]
|
||||
.into_iter()
|
||||
.map(u32::from)
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![1, 5, 6, 7, 8]
|
||||
.into_iter()
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
vec![1].into_iter().flat_map(u32::to_ne_bytes).collect()
|
||||
},
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
|
||||
let client_credentials = client.get_credentials(&state.display_handle).ok();
|
||||
let Some(seat_data) = client.get_data::<ClientState>().map(|s| s.seat.clone())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let xdg_surface = xdg_surface.clone();
|
||||
CoreSurface::add_to(
|
||||
state.display_handle.clone(),
|
||||
&wl_surface,
|
||||
{
|
||||
let wl_surface_resource = wl_surface_resource.clone();
|
||||
move || {
|
||||
let wl_surface = wl_surface_resource.upgrade().unwrap();
|
||||
|
||||
let backend = XdgBackend::create(
|
||||
wl_surface.clone(),
|
||||
toplevel.clone(),
|
||||
seat_data.clone(),
|
||||
);
|
||||
let (node, panel_item) = PanelItem::create(
|
||||
Box::new(backend),
|
||||
client_credentials.map(|c| c.pid),
|
||||
);
|
||||
utils::insert_data(&wl_surface, Arc::downgrade(&panel_item));
|
||||
utils::insert_data_raw(&wl_surface, node);
|
||||
handle_cursor(
|
||||
&panel_item,
|
||||
panel_item.backend.seat.cursor_info_rx.clone(),
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
let wl_surface_resource = wl_surface_resource.clone();
|
||||
move |_| {
|
||||
let wl_surface = wl_surface_resource.upgrade().unwrap();
|
||||
|
||||
let Some(panel_item) =
|
||||
utils::get_data::<PanelItem<XdgBackend>>(&wl_surface)
|
||||
else {
|
||||
let Some(toplevel) = utils::get_data::<XdgToplevel>(&wl_surface)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
// if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates
|
||||
toplevel.configure(
|
||||
0,
|
||||
0,
|
||||
if toplevel.version() >= 2 {
|
||||
vec![5, 6, 7, 8]
|
||||
.into_iter()
|
||||
.flat_map(u32::to_ne_bytes)
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
);
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc());
|
||||
return;
|
||||
};
|
||||
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(size) = core_surface.size() else {
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_size_changed(size);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_surface::Request::GetPopup {
|
||||
id,
|
||||
parent,
|
||||
positioner,
|
||||
} => {
|
||||
let Some(parent) = parent else { return };
|
||||
let Some(parent_wl_surface) = parent
|
||||
.data::<WlWeak<WlSurface>>()
|
||||
.map(WlWeak::upgrade)
|
||||
.map(Result::ok)
|
||||
.flatten()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) =
|
||||
utils::get_data::<Weak<PanelItem<XdgBackend>>>(&parent_wl_surface)
|
||||
.as_deref()
|
||||
.and_then(Weak::upgrade)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let uid = nanoid!();
|
||||
let popup_data = PopupData::new(
|
||||
uid.clone(),
|
||||
parent_wl_surface.clone(),
|
||||
&panel_item,
|
||||
positioner,
|
||||
);
|
||||
handle_cursor(&panel_item, panel_item.backend.seat.cursor_info_rx.clone());
|
||||
let xdg_popup = data_init.init(id, wl_surface.downgrade());
|
||||
utils::insert_data(&wl_surface, SurfaceID::Child(uid));
|
||||
utils::insert_data(&wl_surface, Arc::downgrade(&panel_item));
|
||||
utils::insert_data(&wl_surface, popup_data);
|
||||
utils::insert_data(&wl_surface, xdg_popup.clone());
|
||||
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
|
||||
|
||||
let xdg_surface = xdg_surface.downgrade();
|
||||
let popup_wl_surface = wl_surface.downgrade();
|
||||
CoreSurface::add_to(
|
||||
state.display_handle.clone(),
|
||||
&wl_surface,
|
||||
move || {
|
||||
let Ok(wl_surface) = popup_wl_surface.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(popup_data) = utils::get_data::<PopupData>(&wl_surface) else {
|
||||
return;
|
||||
};
|
||||
panel_item
|
||||
.backend
|
||||
.new_popup(&panel_item, &wl_surface, &*popup_data);
|
||||
},
|
||||
move |commit_count| {
|
||||
if commit_count == 0 {
|
||||
if let Ok(xdg_surface) = xdg_surface.upgrade() {
|
||||
xdg_surface.configure(SERIAL_COUNTER.inc())
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
xdg_surface::Request::SetWindowGeometry {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
} => {
|
||||
debug!(
|
||||
?xdg_surface,
|
||||
x, y, width, height, "Set XDG surface geometry"
|
||||
);
|
||||
let geometry = Geometry {
|
||||
origin: [x, y].into(),
|
||||
size: [width as u32, height as u32].into(),
|
||||
};
|
||||
xdg_surface_data.geometry.lock().replace(geometry);
|
||||
}
|
||||
xdg_surface::Request::AckConfigure { serial } => {
|
||||
debug!(?xdg_surface, serial, "Acknowledge XDG surface configure");
|
||||
}
|
||||
xdg_surface::Request::Destroy => {
|
||||
debug!(?xdg_surface, "Destroy XDG surface");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
use super::{backend::XdgBackend, surface::XdgSurfaceData};
|
||||
use crate::{
|
||||
nodes::items::panel::{Geometry, PanelItem, ToplevelInfo},
|
||||
wayland::{
|
||||
state::WaylandState,
|
||||
surface::CoreSurface,
|
||||
utils::{self, get_data},
|
||||
},
|
||||
};
|
||||
use mint::Vector2;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use smithay::reexports::{
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge, XdgToplevel},
|
||||
wayland_server::{
|
||||
protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, Resource,
|
||||
Weak as WlWeak,
|
||||
},
|
||||
};
|
||||
use std::sync::Weak;
|
||||
use tracing::{debug, error};
|
||||
use wayland_backend::protocol::WEnum;
|
||||
|
||||
pub struct ToplevelData {
|
||||
panel_item: OnceCell<Weak<PanelItem<XdgBackend>>>,
|
||||
wl_surface: WlWeak<WlSurface>,
|
||||
parent: Mutex<Option<WlWeak<WlSurface>>>,
|
||||
title: Mutex<Option<String>>,
|
||||
app_id: Mutex<Option<String>>,
|
||||
max_size: Mutex<Option<Vector2<u32>>>,
|
||||
min_size: Mutex<Option<Vector2<u32>>>,
|
||||
}
|
||||
impl ToplevelData {
|
||||
pub fn new(wl_surface: &WlSurface) -> Self {
|
||||
ToplevelData {
|
||||
panel_item: OnceCell::new(),
|
||||
wl_surface: wl_surface.downgrade(),
|
||||
parent: Mutex::new(None),
|
||||
title: Mutex::new(None),
|
||||
app_id: Mutex::new(None),
|
||||
max_size: Mutex::new(None),
|
||||
min_size: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
pub fn parent(&self) -> Option<WlSurface> {
|
||||
self.parent
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map(WlWeak::upgrade)
|
||||
.map(Result::ok)
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
impl From<&ToplevelData> for ToplevelInfo {
|
||||
fn from(value: &ToplevelData) -> Self {
|
||||
let wl_surface = value.wl_surface.upgrade().ok();
|
||||
let size = CoreSurface::from_wl_surface(wl_surface.as_ref().unwrap())
|
||||
.unwrap()
|
||||
.size()
|
||||
.unwrap();
|
||||
let logical_rectangle = wl_surface
|
||||
.as_ref()
|
||||
.and_then(utils::get_data::<XdgSurfaceData>)
|
||||
.and_then(|d| d.geometry.lock().clone())
|
||||
.unwrap_or_else(|| Geometry {
|
||||
origin: [0, 0].into(),
|
||||
size,
|
||||
});
|
||||
let parent = value
|
||||
.parent()
|
||||
.as_ref()
|
||||
.and_then(utils::get_data::<Weak<PanelItem<XdgBackend>>>)
|
||||
.as_deref()
|
||||
.and_then(Weak::upgrade)
|
||||
.map(|i| i.uid.clone());
|
||||
ToplevelInfo {
|
||||
parent,
|
||||
title: value.title.lock().clone(),
|
||||
app_id: value.app_id.lock().clone(),
|
||||
size,
|
||||
min_size: value.min_size.lock().clone(),
|
||||
max_size: value.max_size.lock().clone(),
|
||||
logical_rectangle,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for ToplevelData {
|
||||
fn drop(&mut self) {
|
||||
// let Some(panel_item) = self.panel_item.get().and_then(Weak::upgrade) else {
|
||||
// return;
|
||||
// };
|
||||
// panel_item.drop_toplevel();
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<XdgToplevel, WlWeak<WlSurface>, WaylandState> for WaylandState {
|
||||
fn request(
|
||||
state: &mut WaylandState,
|
||||
_client: &Client,
|
||||
xdg_toplevel: &XdgToplevel,
|
||||
request: xdg_toplevel::Request,
|
||||
wl_surface_resource: &WlWeak<WlSurface>,
|
||||
_dhandle: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, WaylandState>,
|
||||
) {
|
||||
let Ok(wl_surface) = wl_surface_resource.upgrade() else {
|
||||
error!("Couldn't get the wayland surface of the xdg toplevel");
|
||||
return;
|
||||
};
|
||||
let Some(toplevel_data) = utils::get_data::<ToplevelData>(&wl_surface) else {
|
||||
error!("Couldn't get the XdgToplevel");
|
||||
return;
|
||||
};
|
||||
match request {
|
||||
xdg_toplevel::Request::SetParent { parent } => {
|
||||
debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent");
|
||||
let Some(parent_xdg_toplevel) = parent else {
|
||||
*toplevel_data.parent.lock() = None;
|
||||
return;
|
||||
};
|
||||
let Some(parent_toplevel_data) = parent_xdg_toplevel.data::<ToplevelData>() else {
|
||||
error!("Couldn't get XDG toplevel parent data");
|
||||
return;
|
||||
};
|
||||
let Ok(parent_wl_surface) = parent_toplevel_data.wl_surface.upgrade() else {
|
||||
error!("Couldn't get XDG toplevel parent wl surface");
|
||||
return;
|
||||
};
|
||||
*toplevel_data.parent.lock() = Some(parent_wl_surface.downgrade());
|
||||
let Some(parent_panel_item) = parent_toplevel_data
|
||||
.panel_item
|
||||
.get()
|
||||
.and_then(Weak::upgrade)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_parent_changed(&parent_panel_item.uid);
|
||||
}
|
||||
xdg_toplevel::Request::SetTitle { title } => {
|
||||
debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title");
|
||||
*toplevel_data.title.lock() = (!title.is_empty()).then_some(title.clone());
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_title_changed(&title);
|
||||
}
|
||||
xdg_toplevel::Request::SetAppId { app_id } => {
|
||||
debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID");
|
||||
*toplevel_data.app_id.lock() = (!app_id.is_empty()).then_some(app_id.clone());
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_app_id_changed(&app_id);
|
||||
}
|
||||
xdg_toplevel::Request::Move { seat, serial } => {
|
||||
debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request");
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_move_request();
|
||||
}
|
||||
xdg_toplevel::Request::Resize {
|
||||
seat,
|
||||
serial,
|
||||
edges,
|
||||
} => {
|
||||
let WEnum::Value(edges) = edges else { return };
|
||||
debug!(
|
||||
?xdg_toplevel,
|
||||
?seat,
|
||||
serial,
|
||||
?edges,
|
||||
"XDG Toplevel resize request"
|
||||
);
|
||||
let (up, down, left, right) = match edges {
|
||||
ResizeEdge::Top => (true, false, false, false),
|
||||
ResizeEdge::Bottom => (false, true, false, false),
|
||||
ResizeEdge::Left => (false, false, true, false),
|
||||
ResizeEdge::TopLeft => (true, false, true, false),
|
||||
ResizeEdge::BottomLeft => (false, true, true, false),
|
||||
ResizeEdge::Right => (false, false, false, true),
|
||||
ResizeEdge::TopRight => (true, false, false, true),
|
||||
ResizeEdge::BottomRight => (false, true, false, true),
|
||||
_ => (false, false, false, false),
|
||||
};
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.toplevel_resize_request(up, down, left, right)
|
||||
}
|
||||
xdg_toplevel::Request::SetMaxSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size");
|
||||
*toplevel_data.max_size.lock() = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetMinSize { width, height } => {
|
||||
debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size");
|
||||
*toplevel_data.min_size.lock() = (width > 1 || height > 1)
|
||||
.then_some(Vector2::from([width as u32, height as u32]));
|
||||
}
|
||||
xdg_toplevel::Request::SetFullscreen { output: _ } => {
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.toplevel_state.lock().fullscreen = true;
|
||||
panel_item.backend.configure(None);
|
||||
panel_item.toplevel_fullscreen_active(true);
|
||||
}
|
||||
xdg_toplevel::Request::UnsetFullscreen => {
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.backend.toplevel_state.lock().fullscreen = false;
|
||||
panel_item.backend.configure(None);
|
||||
panel_item.toplevel_fullscreen_active(false);
|
||||
}
|
||||
xdg_toplevel::Request::Destroy => {
|
||||
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
|
||||
let Some(panel_item) = get_data::<PanelItem<XdgBackend>>(&wl_surface) else {
|
||||
error!("Couldn't get the panel item");
|
||||
return;
|
||||
};
|
||||
panel_item.drop_toplevel();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user