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",
|
"directories",
|
||||||
"fxtypemap",
|
"fxtypemap",
|
||||||
"glam 0.23.0",
|
"glam 0.23.0",
|
||||||
"global_counter",
|
|
||||||
"input-event-codes",
|
"input-event-codes",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ serde = { version = "1.0.160", features = ["derive"] }
|
|||||||
serde_repr = "0.1.16"
|
serde_repr = "0.1.16"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||||
global_counter = "0.2.2"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
xkbcommon = { version = "0.7.0", default-features = false, optional = true }
|
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 self::{state::WaylandState, surface::CORE_SURFACES};
|
||||||
use crate::{core::task, wayland::state::ClientState};
|
use crate::{core::task, wayland::state::ClientState};
|
||||||
use color_eyre::eyre::{ensure, Result};
|
use color_eyre::eyre::{ensure, Result};
|
||||||
use global_counter::primitive::exact::CounterU32;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use sk::StereoKitDraw;
|
use sk::StereoKitDraw;
|
||||||
@@ -52,7 +51,6 @@ use tracing::{debug_span, info, instrument};
|
|||||||
|
|
||||||
pub static X_DISPLAY: OnceCell<u32> = OnceCell::new();
|
pub static X_DISPLAY: OnceCell<u32> = OnceCell::new();
|
||||||
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
pub static WAYLAND_DISPLAY: OnceCell<String> = OnceCell::new();
|
||||||
pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0);
|
|
||||||
|
|
||||||
struct EGLRawHandles {
|
struct EGLRawHandles {
|
||||||
display: *const c_void,
|
display: *const c_void,
|
||||||
@@ -175,6 +173,7 @@ impl Wayland {
|
|||||||
acc = listen_async.accept() => { // New client connected
|
acc = listen_async.accept() => { // New client connected
|
||||||
let (stream, _) = acc?;
|
let (stream, _) = acc?;
|
||||||
let client_state = Arc::new(ClientState {
|
let client_state = Arc::new(ClientState {
|
||||||
|
pid: stream.peer_cred().ok().and_then(|c| c.pid()),
|
||||||
id: OnceCell::new(),
|
id: OnceCell::new(),
|
||||||
compositor_state: Default::default(),
|
compositor_state: Default::default(),
|
||||||
display: Arc::downgrade(&display),
|
display: Arc::downgrade(&display),
|
||||||
|
|||||||
@@ -106,15 +106,49 @@ impl SeatWrapper {
|
|||||||
touches: Mutex::new(FxHashMap::default()),
|
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>) {
|
pub fn pointer_motion(&self, surface: WlSurface, position: Vector2<f32>) {
|
||||||
let Some(state) = self.wayland_state.upgrade() else {
|
let Some(state) = self.wayland_state.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let mut state = state.lock();
|
||||||
let Some(pointer) = self.seat.get_pointer() else {
|
let Some(pointer) = self.seat.get_pointer() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
pointer.motion(
|
pointer.motion(
|
||||||
&mut state.lock(),
|
&mut state,
|
||||||
Some((surface, (0, 0).into())),
|
Some((surface, (0, 0).into())),
|
||||||
&MotionEvent {
|
&MotionEvent {
|
||||||
location: (position.x as f64, position.y as f64).into(),
|
location: (position.x as f64, position.y as f64).into(),
|
||||||
@@ -122,16 +156,18 @@ impl SeatWrapper {
|
|||||||
time: 0,
|
time: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
pointer.frame(&mut state);
|
||||||
}
|
}
|
||||||
pub fn pointer_button(&self, button: u32, pressed: bool) {
|
pub fn pointer_button(&self, button: u32, pressed: bool) {
|
||||||
let Some(state) = self.wayland_state.upgrade() else {
|
let Some(state) = self.wayland_state.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let mut state = state.lock();
|
||||||
let Some(pointer) = self.seat.get_pointer() else {
|
let Some(pointer) = self.seat.get_pointer() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
pointer.button(
|
pointer.button(
|
||||||
&mut state.lock(),
|
&mut state,
|
||||||
&ButtonEvent {
|
&ButtonEvent {
|
||||||
button,
|
button,
|
||||||
state: if pressed {
|
state: if pressed {
|
||||||
@@ -143,6 +179,7 @@ impl SeatWrapper {
|
|||||||
time: 0,
|
time: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
pointer.frame(&mut state);
|
||||||
}
|
}
|
||||||
pub fn pointer_scroll(
|
pub fn pointer_scroll(
|
||||||
&self,
|
&self,
|
||||||
@@ -171,7 +208,8 @@ impl SeatWrapper {
|
|||||||
v120: scroll_steps.map(|d| ((d.x * 120.0) as i32, (d.y * 120.0) as i32)),
|
v120: scroll_steps.map(|d| ((d.x * 120.0) as i32, (d.y * 120.0) as i32)),
|
||||||
stop: (false, false),
|
stop: (false, false),
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
|
pointer.frame(&mut state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keyboard_keys(&self, surface: WlSurface, keymap_id: &str, keys: Vec<i32>) {
|
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},
|
input::{keyboard::XkbConfig, SeatState},
|
||||||
output::{Mode, Output, Scale, Subpixel},
|
output::{Mode, Output, Scale, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg::{
|
wayland_protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
|
||||||
decoration::zv1::server::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
|
|
||||||
shell::server::xdg_wm_base::XdgWmBase,
|
|
||||||
},
|
|
||||||
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as DecorationMode,
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
backend::{ClientData, ClientId, DisconnectReason},
|
backend::{ClientData, ClientId, DisconnectReason},
|
||||||
@@ -34,7 +31,7 @@ use smithay::{
|
|||||||
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
self, DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState,
|
||||||
},
|
},
|
||||||
output::OutputHandler,
|
output::OutputHandler,
|
||||||
shell::kde::decoration::KdeDecorationState,
|
shell::{kde::decoration::KdeDecorationState, xdg::XdgShellState},
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -43,6 +40,7 @@ use tokio::sync::mpsc::UnboundedSender;
|
|||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
|
pub pid: Option<i32>,
|
||||||
pub id: OnceCell<ClientId>,
|
pub id: OnceCell<ClientId>,
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
pub display: Weak<DisplayWrapper>,
|
pub display: Weak<DisplayWrapper>,
|
||||||
@@ -83,6 +81,7 @@ pub struct WaylandState {
|
|||||||
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
pub dmabuf_tx: UnboundedSender<(Dmabuf, Option<dmabuf::ImportNotifier>)>,
|
||||||
pub seat_state: SeatState<Self>,
|
pub seat_state: SeatState<Self>,
|
||||||
pub seat: Arc<SeatWrapper>,
|
pub seat: Arc<SeatWrapper>,
|
||||||
|
pub xdg_shell: XdgShellState,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,8 +162,9 @@ impl WaylandState {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
output.set_preferred(mode);
|
output.set_preferred(mode);
|
||||||
|
|
||||||
|
let xdg_shell = XdgShellState::new::<Self>(&display_handle);
|
||||||
display_handle.create_global::<Self, WlDataDeviceManager, _>(3, ());
|
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, ZxdgDecorationManagerV1, _>(1, ());
|
||||||
display_handle.create_global::<Self, WlDrm, _>(2, ());
|
display_handle.create_global::<Self, WlDrm, _>(2, ());
|
||||||
|
|
||||||
@@ -184,6 +184,7 @@ impl WaylandState {
|
|||||||
dmabuf_tx,
|
dmabuf_tx,
|
||||||
seat_state,
|
seat_state,
|
||||||
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
seat: Arc::new(SeatWrapper::new(weak.clone(), seat)),
|
||||||
|
xdg_shell,
|
||||||
output,
|
output,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ pub struct CoreSurface {
|
|||||||
sk_tex: OnceCell<Tex>,
|
sk_tex: OnceCell<Tex>,
|
||||||
sk_mat: OnceCell<Arc<Material>>,
|
sk_mat: OnceCell<Arc<Material>>,
|
||||||
material_offset: Mutex<Delta<u32>>,
|
material_offset: Mutex<Delta<u32>>,
|
||||||
on_mapped: Box<dyn Fn() + Send + Sync>,
|
on_mapped: Mutex<Box<dyn Fn() + Send + Sync>>,
|
||||||
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
on_commit: Mutex<Box<dyn Fn(u32) + Send + Sync>>,
|
||||||
pub pending_material_applications: Registry<ModelPart>,
|
pub pending_material_applications: Registry<ModelPart>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +64,8 @@ impl CoreSurface {
|
|||||||
sk_tex: OnceCell::new(),
|
sk_tex: OnceCell::new(),
|
||||||
sk_mat: OnceCell::new(),
|
sk_mat: OnceCell::new(),
|
||||||
material_offset: Mutex::new(Delta::new(0)),
|
material_offset: Mutex::new(Delta::new(0)),
|
||||||
on_mapped: Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>,
|
on_mapped: Mutex::new(Box::new(on_mapped) as Box<dyn Fn() + Send + Sync>),
|
||||||
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
on_commit: Mutex::new(Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>),
|
||||||
pending_material_applications: Registry::new(),
|
pending_material_applications: Registry::new(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -73,13 +73,18 @@ impl CoreSurface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&self, count: u32) {
|
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>> {
|
pub fn from_wl_surface(surf: &WlSurface) -> Option<Arc<CoreSurface>> {
|
||||||
get_data(surf)
|
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) {
|
pub fn process(&self, sk: &impl StereoKitDraw, renderer: &mut GlesRenderer) {
|
||||||
let Some(wl_surface) = self.wl_surface() else {
|
let Some(wl_surface) = self.wl_surface() else {
|
||||||
return;
|
return;
|
||||||
@@ -170,7 +175,7 @@ impl CoreSurface {
|
|||||||
});
|
});
|
||||||
drop(mapped_data);
|
drop(mapped_data);
|
||||||
if just_mapped {
|
if just_mapped {
|
||||||
(self.on_mapped)();
|
(*self.on_mapped.lock())();
|
||||||
}
|
}
|
||||||
self.apply_surface_materials();
|
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