refactor: panel items

This commit is contained in:
Nova
2023-08-22 05:22:08 -04:00
parent 10034cdab2
commit de0295fbf8
8 changed files with 784 additions and 515 deletions

5
Cargo.lock generated
View File

@@ -1918,9 +1918,9 @@ dependencies = [
[[package]]
name = "serde_repr"
version = "0.1.15"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e168eaaf71e8f9bd6037feb05190485708e019f4fd87d161b3c0a0d37daf85e5"
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
@@ -2081,6 +2081,7 @@ dependencies = [
"rustc-hash",
"send_wrapper",
"serde",
"serde_repr",
"smithay",
"stardust-xr",
"stereokit",

View File

@@ -55,6 +55,7 @@ xkbcommon = { version = "0.5.0", default-features = false, optional = true }
stardust-xr = "0.14.0"
directories = "5.0.0"
serde = { version = "1.0.160", features = ["derive"] }
serde_repr = "0.1.16"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
global_counter = "0.2.2"

View File

@@ -52,7 +52,7 @@ pub fn startup_settings(env: &FxHashMap<String, String>) -> Option<StartupSettin
}
pub struct Client {
pid: Option<i32>,
pub pid: Option<i32>,
// env: Option<FxHashMap<String, String>>,
exe: Option<PathBuf>,
dispatch_join_handle: OnceCell<JoinHandle<Result<()>>>,

View File

@@ -20,6 +20,7 @@ use serde::{
ser::Serializer,
Deserialize, Serialize,
};
use serde_repr::{Deserialize_repr, Serialize_repr};
use stardust_xr::schemas::flex::{deserialize, serialize};
use std::sync::{Arc, Weak};
use tracing::debug;
@@ -29,20 +30,31 @@ lazy_static! {
type_name: "panel",
aliased_local_signals: vec![
"apply_surface_material",
"configure_toplevel",
"set_toplevel_capabilities",
"close_toplevel",
"auto_size_toplevel",
"set_toplevel_size_changed",
"set_toplevel_state",
"set_toplevel_tiling",
"set_toplevel_bounds",
"set_maximize_enabled",
"set_minimize_enabled",
"set_fullscreen_enabled",
"set_window_menu_enabled",
"pointer_scroll",
"pointer_button",
"pointer_motion",
"keyboard_key",
"keyboard_set_keymap_names",
"keyboard_set_keymap_string",
"close",
],
aliased_local_methods: vec![],
aliased_remote_signals: vec![
"commit_toplevel",
"toplevel_parent_changed",
"toplevel_title_changed",
"toplevel_app_id_changed",
"toplevel_window_menu",
"recommend_toplevel_state",
"toplevel_move_request",
"toplevel_resize_request",
"toplevel_size_changed",
"set_cursor",
"new_child",
"reposition_child",
@@ -118,28 +130,101 @@ impl serde::Serialize for SurfaceID {
}
}
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(tag = "type", content = "content")]
pub enum RecommendedState {
Maximize(bool),
Fullscreen(bool),
Minimize,
Move,
Resize(u32),
/// The origin and size of the surface's "solid" part.
#[derive(Debug, Serialize, Clone, Copy)]
pub struct Geometry {
pub origin: Vector2<i32>,
pub size: Vector2<u32>,
}
/// The state of the panel item's toplevel.
#[derive(Debug, Clone, Serialize)]
pub struct ToplevelInfo {
/// The UID of the panel item of the parent of this toplevel, if it exists
pub parent: Option<String>,
/// Equivalent to the window title
pub title: Option<String>,
/// Application identifier, see <https://standards.freedesktop.org/desktop-entry-spec/>
pub app_id: Option<String>,
/// Current size in pixels
pub size: Vector2<u32>,
/// Recommended minimum size in pixels
pub min_size: Option<Vector2<u32>>,
/// Recommended maximum size in pixels
pub max_size: Option<Vector2<u32>>,
/// Surface geometry
pub logical_rectangle: Geometry,
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
pub enum ToplevelState {
Floating,
Minimized,
UnMinimized,
Maximized,
UnMaximized,
Fullscreen,
UnFullscreen,
}
/// Data on positioning a child
#[derive(Debug, Clone, Serialize)]
pub struct ChildInfo {
pub uid: String,
pub parent: SurfaceID,
pub geometry: Geometry,
}
#[derive(Debug, Clone, Deserialize_repr)]
#[repr(u32)]
pub enum Edge {
None = 0,
Top = 1,
Bottom = 2,
Left = 4,
TopLeft = 5,
BottomLeft = 6,
Right = 8,
TopRight = 9,
BottomRight = 10,
}
/// The init data for the panel item.
#[derive(Debug, Clone, Serialize)]
pub struct PanelItemInitData {
/// The cursor, if applicable.
pub cursor: Option<Geometry>,
/// Size of the toplevel surface in pixels.
pub toplevel: ToplevelInfo,
/// Vector of childs that already exist
pub children: Vec<ChildInfo>,
/// The surface, if any, that has exclusive input to the pointer.
pub pointer_grab: Option<SurfaceID>,
/// The surface, if any, that has exclusive input to the keyboard.
pub keyboard_grab: Option<SurfaceID>,
}
pub trait Backend: Send + Sync + 'static {
fn serialize_start_data(&self, id: &str) -> Result<Message>;
fn serialize_toplevel(&self) -> Result<Message>;
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>);
fn configure_toplevel(
&self,
size: Option<Vector2<u32>>,
states: Vec<u32>,
bounds: Option<Vector2<u32>>,
);
fn start_data(&self) -> Result<PanelItemInitData>;
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>);
fn close_toplevel(&self);
fn auto_size_toplevel(&self);
fn set_toplevel_size(&self, size: Vector2<u32>);
fn toplevel_maximize(&self);
fn toplevel_unmaximize(&self);
fn toplevel_fullscreen(&self);
fn toplevel_unfullscreen(&self);
fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool);
fn set_toplevel_bounds(&self, bounds: Option<Vector2<u32>>);
fn set_toplevel_focused_visuals(&self, focused: bool);
fn set_maximize_enabled(&self, enabled: bool);
fn set_minimize_enabled(&self, enabled: bool);
fn set_fullscreen_enabled(&self, enabled: bool);
fn set_window_menu_enabled(&self, enabled: bool);
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>);
fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool);
fn pointer_scroll(
@@ -149,7 +234,6 @@ pub trait Backend: Send + Sync + 'static {
scroll_steps: Option<Vector2<f32>>,
);
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()>;
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool);
}
@@ -160,7 +244,7 @@ pub fn panel_item_from_node(node: &Node) -> Option<Arc<dyn PanelItemTrait>> {
pub trait PanelItemTrait: Backend + Send + Sync + 'static {
fn uid(&self) -> &str;
// fn node(&self) -> Option<Arc<Node>>;
fn serialize_start_data(&self, id: &str) -> Result<Message>;
}
pub struct PanelItem<B: Backend + ?Sized> {
@@ -210,20 +294,34 @@ impl<B: Backend + ?Sized> PanelItem<B> {
items::capture(&item, &acceptor);
}
}
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
node.add_local_signal("configure_toplevel", Self::configure_toplevel_flex);
node.add_local_signal(
"set_toplevel_capabilities",
Self::set_toplevel_capabilities_flex,
);
node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex);
node.add_local_signal("pointer_button", Self::pointer_button_flex);
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex);
node.add_local_signal("close_toplevel", Self::close_toplevel_flex);
node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex);
node.add_local_signal("set_toplevel_size_changed", Self::set_toplevel_size_changed);
node.add_local_signal("toplevel_maximize", Self::toplevel_maximize_flex);
node.add_local_signal("toplevel_unmaximize", Self::toplevel_unmaximize_flex);
node.add_local_signal("toplevel_fullscreen", Self::toplevel_fullscreen_flex);
node.add_local_signal("toplevel_unfullscreen", Self::toplevel_unfullscreen_flex);
node.add_local_signal("set_toplevel_tiling", Self::set_toplevel_tiling_flex);
node.add_local_signal("set_toplevel_bounds", Self::set_toplevel_bounds_flex);
node.add_local_signal("set_maximize_enabled", Self::set_maximize_enabled_flex);
node.add_local_signal("set_minimize_enabled", Self::set_minimize_enabled_flex);
node.add_local_signal("set_fullscreen_enabled", Self::set_fullscreen_enabled_flex);
node.add_local_signal(
"keyboard_set_keymap_string",
Self::keyboard_set_keymap_string_flex,
"set_window_menu_enabled",
Self::set_window_menu_enabled_flex,
);
node.add_local_signal("pointer_motion", Self::pointer_motion_flex);
node.add_local_signal("pointer_button", Self::pointer_button_flex);
node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex);
// node.add_local_signal(
// "keyboard_set_keymap_string",
// Self::keyboard_set_keymap_string_flex,
// );
// node.add_local_signal(
// "keyboard_set_keymap_names",
// Self::keyboard_set_keymap_names_flex,
@@ -232,11 +330,88 @@ impl<B: Backend + ?Sized> PanelItem<B> {
(node, panel_item)
}
pub fn drop_toplevel(&self) {
let Some(node) = self.node.upgrade() else {return};
node.destroy();
}
}
pub fn node(&self) -> Option<Arc<Node>> {
self.node.upgrade()
// Remote signals
impl<B: Backend + ?Sized> PanelItem<B> {
pub fn toplevel_parent_changed(&self, parent: &str) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap());
}
pub fn toplevel_title_changed(&self, title: &str) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap());
}
pub fn toplevel_app_id_changed(&self, app_id: &str) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap());
}
pub fn toplevel_window_menu(&self, offset: Vector2<i32>) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_window_menu", serialize(offset).unwrap());
}
pub fn recommend_toplevel_state(&self, state: ToplevelState) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("recommend_toplevel_state", serialize(state).unwrap());
}
pub fn toplevel_move_request(&self) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_move_request", Vec::<u8>::new());
}
pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal(
"toplevel_resize_request",
serialize((up, down, left, right)).unwrap(),
);
}
pub fn toplevel_size_changed(&self, size: Vector2<u32>) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap());
}
pub fn set_cursor(&self, geometry: Option<Geometry>) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap());
}
pub fn new_child(&self, info: ChildInfo) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("new_child", serialize(info).unwrap());
}
pub fn reposition_child(&self, uid: &str, geometry: Geometry) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap());
}
pub fn drop_child(&self, uid: &str) {
let Some(node) = self.node.upgrade() else {return};
let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap());
}
}
// Local signals
macro_rules! flex_no_args {
($fn_name: ident, $trait_fn: ident) => {
fn $fn_name(node: &Node, _calling_client: Arc<Client>, _message: Message) -> Result<()> {
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
panel_item.$trait_fn();
Ok(())
}
};
}
macro_rules! flex_deserialize {
($fn_name: ident, $trait_fn: ident) => {
fn $fn_name(node: &Node, _calling_client: Arc<Client>, message: Message) -> Result<()> {
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
panel_item.$trait_fn(deserialize(message.as_ref())?);
Ok(())
}
};
}
impl<B: Backend + ?Sized> PanelItem<B> {
fn apply_surface_material_flex(
node: &Node,
calling_client: Arc<Client>,
@@ -264,6 +439,30 @@ impl<B: Backend + ?Sized> PanelItem<B> {
Ok(())
}
flex_no_args!(close_toplevel_flex, close_toplevel);
flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel);
flex_deserialize!(set_toplevel_size_changed, set_toplevel_size);
flex_no_args!(toplevel_maximize_flex, toplevel_maximize);
flex_no_args!(toplevel_unmaximize_flex, toplevel_unmaximize);
flex_no_args!(toplevel_fullscreen_flex, toplevel_fullscreen);
flex_no_args!(toplevel_unfullscreen_flex, toplevel_unfullscreen);
flex_deserialize!(set_toplevel_bounds_flex, set_toplevel_bounds);
fn set_toplevel_tiling_flex(
node: &Node,
_calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
let (up, down, left, right) = deserialize(message.as_ref())?;
panel_item.set_toplevel_tiling(up, down, left, right);
Ok(())
}
flex_deserialize!(set_maximize_enabled_flex, set_maximize_enabled);
flex_deserialize!(set_minimize_enabled_flex, set_minimize_enabled);
flex_deserialize!(set_fullscreen_enabled_flex, set_fullscreen_enabled);
flex_deserialize!(set_window_menu_enabled_flex, set_window_menu_enabled);
fn pointer_motion_flex(
node: &Node,
_calling_client: Arc<Client>,
@@ -312,65 +511,6 @@ impl<B: Backend + ?Sized> PanelItem<B> {
Ok(())
}
fn keyboard_set_keymap_string_flex(
node: &Node,
_calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let keymap_string: &str = deserialize(message.as_ref())?;
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
debug!("Keyboard set keymap");
panel_item.keyboard_set_keymap(keymap_string)
// PanelItem::keyboard_set_keymap_flex(node, &keymap)
}
// fn keyboard_set_keymap_names_flex(
// node: &Node,
// _calling_client: Arc<Client>,
// message: Message,
// ) -> Result<()> {
// #[derive(Debug, Deserialize)]
// struct Names<'a> {
// rules: &'a str,
// model: &'a str,
// layout: &'a str,
// variant: &'a str,
// options: Option<String>,
// }
// let names: Names = deserialize(message.as_ref())?;
// let context = xkb::Context::new(0);
// let keymap = Keymap::new_from_names(
// &context,
// names.rules,
// names.model,
// names.layout,
// names.variant,
// names.options,
// XKB_KEYMAP_FORMAT_TEXT_V1,
// )
// .ok_or_else(|| eyre!("Keymap is not valid"))?;
// PanelItem::keyboard_set_keymap_flex(node, &keymap)
// }
// fn keyboard_set_keymap_flex(node: &Node, keymap: &str) -> Result<()> {
// let Some(panel_item): Option<Arc<PanelItem<dyn WaylandBackend>>> = panel_item_from_node(node) else { return Ok(()) };
// debug!("Keyboard set keymap");
// panel_item.seat_data.set_keymap(
// keymap,
// match &panel_item {
// Backend::Wayland(w) => w.input_surfaces(),
// #[cfg(feature = "xwayland")]
// Backend::X11(_) => panel_item
// .toplevel_wl_surface()
// .map(|s| vec![s])
// .unwrap_or_default(),
// },
// );
// Ok(())
// }
fn keyboard_key_flex(
node: &Node,
_calling_client: Arc<Client>,
@@ -390,89 +530,69 @@ impl<B: Backend + ?Sized> PanelItem<B> {
let Ok(message) = serialize(sid) else {return};
let _ = node.send_remote_signal("grab_keyboard", message);
}
fn configure_toplevel_flex(
node: &Node,
_calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
#[derive(Debug, Deserialize)]
struct ConfigureToplevelInfo {
size: Option<Vector2<u32>>,
states: Vec<u32>,
bounds: Option<Vector2<u32>>,
}
let info: ConfigureToplevelInfo = deserialize(message.as_ref())?;
panel_item.configure_toplevel(info.size, info.states, info.bounds);
Ok(())
}
fn set_toplevel_capabilities_flex(
node: &Node,
_calling_client: Arc<Client>,
message: Message,
) -> Result<()> {
let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) };
let capabilities: Vec<u8> = deserialize(message.as_ref())?;
debug!("Set toplevel capabilities");
panel_item.set_toplevel_capabilities(capabilities);
Ok(())
}
pub fn commit_toplevel(&self) {
debug!("Commit toplevel");
let Some(node) = self.node.upgrade() else {return};
let Ok(data) = self.backend.serialize_toplevel() else {return};
let _ = node.send_remote_signal("commit_toplevel", data);
}
pub fn recommend_toplevel_state(&self, state: RecommendedState) {
let Some(node) = self.node.upgrade() else {return};
let data = serialize(state).unwrap();
debug!(?state, "Recommend toplevel state");
let _ = node.send_remote_signal("recommend_toplevel_state", data);
}
pub fn drop_toplevel(&self) {
let Some(node) = self.node.upgrade() else {return};
node.destroy();
}
}
impl<B: Backend + ?Sized> PanelItemTrait for PanelItem<B> {
fn uid(&self) -> &str {
&self.uid
}
fn serialize_start_data(&self, id: &str) -> Result<Message> {
Ok(serialize((id, self.start_data()?))?.into())
}
}
impl<B: Backend + ?Sized> Backend for PanelItem<B> {
fn serialize_start_data(&self, id: &str) -> Result<Message> {
self.backend.serialize_start_data(id)
}
fn serialize_toplevel(&self) -> Result<Message> {
self.backend.serialize_toplevel()
}
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
self.backend.set_toplevel_capabilities(capabilities)
}
fn configure_toplevel(
&self,
size: Option<Vector2<u32>>,
states: Vec<u32>,
bounds: Option<Vector2<u32>>,
) {
self.backend.configure_toplevel(size, states, bounds)
fn start_data(&self) -> Result<PanelItemInitData> {
self.backend.start_data()
}
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
self.backend.apply_surface_material(surface, model_part)
}
fn close_toplevel(&self) {
self.backend.close_toplevel()
}
fn auto_size_toplevel(&self) {
self.backend.auto_size_toplevel()
}
fn set_toplevel_size(&self, size: Vector2<u32>) {
self.backend.set_toplevel_size(size)
}
fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool) {
self.backend.set_toplevel_tiling(up, down, left, right)
}
fn set_toplevel_bounds(&self, bounds: Option<Vector2<u32>>) {
self.backend.set_toplevel_bounds(bounds)
}
fn set_toplevel_focused_visuals(&self, focused: bool) {
self.backend.set_toplevel_focused_visuals(focused)
}
fn toplevel_maximize(&self) {
self.backend.toplevel_maximize()
}
fn toplevel_unmaximize(&self) {
self.backend.toplevel_unmaximize()
}
fn toplevel_fullscreen(&self) {
self.backend.toplevel_fullscreen()
}
fn toplevel_unfullscreen(&self) {
self.backend.toplevel_unfullscreen()
}
fn set_maximize_enabled(&self, enabled: bool) {
self.backend.set_maximize_enabled(enabled)
}
fn set_minimize_enabled(&self, enabled: bool) {
self.backend.set_minimize_enabled(enabled)
}
fn set_fullscreen_enabled(&self, enabled: bool) {
self.backend.set_fullscreen_enabled(enabled)
}
fn set_window_menu_enabled(&self, enabled: bool) {
self.backend.set_window_menu_enabled(enabled)
}
fn pointer_motion(&self, surface: &SurfaceID, position: Vector2<f32>) {
self.backend.pointer_motion(surface, position)
}
@@ -489,9 +609,6 @@ impl<B: Backend + ?Sized> Backend for PanelItem<B> {
.pointer_scroll(surface, scroll_distance, scroll_steps)
}
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
self.backend.keyboard_set_keymap(keymap)
}
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
self.backend.keyboard_key(surface, key, state)
}

View File

@@ -173,6 +173,19 @@ impl Node {
.store(deserialize(message.as_ref())?, Ordering::Relaxed);
Ok(())
}
// very much up for debate if we should allow this, as you can match objects using this
// pub fn get_client_pid_flex(
// node: &Node,
// _calling_client: Arc<Client>,
// _message: Message,
// ) -> Result<Message> {
// let client = node
// .client
// .upgrade()
// .ok_or_else(|| eyre!("Could not get client for node?"))?;
// let pid = client.pid.ok_or_else(|| eyre!("Client PID is unknown"))?;
// Ok(serialize(pid)?.into())
// }
pub fn destroy_flex(
node: &Node,
_calling_client: Arc<Client>,

View File

@@ -3,8 +3,11 @@ use super::{
surface::CoreSurface,
GLOBAL_DESTROY_QUEUE, SERIAL_COUNTER,
};
use crate::core::task;
use color_eyre::eyre::{eyre, Result};
use crate::{
core::task,
nodes::items::panel::{Backend, Geometry, PanelItem},
};
use color_eyre::eyre::Result;
use mint::Vector2;
use nanoid::nanoid;
use once_cell::sync::OnceCell;
@@ -12,7 +15,7 @@ use parking_lot::Mutex;
use rand::{seq::IteratorRandom, thread_rng};
use rustc_hash::{FxHashMap, FxHashSet};
use smithay::{
input::keyboard::{KeymapFile, ModifiersState},
input::keyboard::ModifiersState,
reexports::wayland_server::{
backend::{ClientId, GlobalId, ObjectId},
protocol::{
@@ -33,10 +36,23 @@ use std::{
};
use tokio::sync::watch;
use tracing::{debug, warn};
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
use xkbcommon::xkb::{self, Context, 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: KeymapFile,
state: xkb::State,
mods: ModifiersState,
keys: FxHashSet<u32>,
@@ -45,7 +61,6 @@ impl KeyboardInfo {
pub fn new(keymap: &Keymap) -> Self {
KeyboardInfo {
state: xkb::State::new(keymap),
keymap: KeymapFile::new(keymap),
mods: ModifiersState::default(),
keys: FxHashSet::default(),
}
@@ -101,7 +116,6 @@ pub enum PointerEvent {
}
#[derive(Debug, Clone)]
pub enum KeyboardEvent {
Keymap,
Key { key: u32, state: u32 },
}
@@ -112,7 +126,7 @@ struct SurfaceInfo {
pointer_queue: VecDeque<PointerEvent>,
pointer_latest_event: Instant,
keyboard_queue: VecDeque<KeyboardEvent>,
keyboard_info: Option<KeyboardInfo>,
keyboard_info: KeyboardInfo,
}
impl SurfaceInfo {
fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender<Option<CursorInfo>>) -> Self {
@@ -122,7 +136,9 @@ impl SurfaceInfo {
pointer_queue: VecDeque::new(),
pointer_latest_event: Instant::now(),
keyboard_queue: VecDeque::new(),
keyboard_info: None,
keyboard_info: KeyboardInfo::new(
&Keymap::new_from_names(&Context::new(0), "", "", "", "", None, 0).unwrap(),
),
}
}
fn flush(&self) {
@@ -217,25 +233,20 @@ impl SurfaceInfo {
locked
}
fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool {
fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, 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 let Ok(key_count) = self.keyboard_info.process(key, state, keyboard) {
if key_count == 0 {
keyboard.leave(SERIAL_COUNTER.inc(), &focus);
return false;
@@ -279,29 +290,6 @@ impl SeatData {
seat_data
}
pub fn set_keymap_str(&self, keymap: &str, surfaces: Vec<WlSurface>) -> Result<()> {
let context = xkb::Context::new(0);
let keymap =
Keymap::new_from_string(&context, keymap.to_string(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
.ok_or_else(|| eyre!("Keymap is not valid"))?;
self.set_keymap(&keymap, surfaces);
Ok(())
}
pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec<WlSurface>) {
let mut panels = self.surfaces.lock();
let Some((_, focus)) = self.keyboard.get() else {return};
for surface in surfaces {
let Some(surface_info) = panels.get_mut(&surface.id()) else {continue};
surface_info
.keyboard_info
.replace(KeyboardInfo::new(keymap));
if *focus.lock() == surface.id() {
surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap);
}
}
}
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};
@@ -357,7 +345,6 @@ impl SeatData {
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())
@@ -414,9 +401,12 @@ pub struct CursorInfo {
pub hotspot_y: i32,
}
impl CursorInfo {
pub fn cursor_data(&self) -> Option<(Vector2<u32>, Vector2<i32>)> {
pub fn cursor_data(&self) -> Option<Geometry> {
let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?;
Some((cursor_size, [self.hotspot_x, self.hotspot_y].into()))
Some(Geometry {
origin: [self.hotspot_x, self.hotspot_y].into(),
size: cursor_size,
})
}
}

View File

@@ -4,23 +4,30 @@ use super::{
surface::CoreSurface,
SERIAL_COUNTER,
};
use crate::nodes::{
drawable::model::ModelPart,
items::panel::{Backend, PanelItem, RecommendedState, SurfaceID},
Message, Node,
use crate::{
nodes::{
drawable::model::ModelPart,
items::panel::{
Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo,
ToplevelState,
},
Node,
},
wayland::seat::handle_cursor,
};
use color_eyre::eyre::{eyre, Result};
use color_eyre::eyre::{bail, eyre, Result};
use mint::Vector2;
use nanoid::nanoid;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::{ser::SerializeSeq, Serialize, Serializer};
use smithay::reexports::{
wayland_protocols::xdg::shell::server::{
xdg_popup::{self, XdgPopup},
xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner},
xdg_surface::{self, XdgSurface},
xdg_toplevel::{self, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE},
xdg_toplevel::{
self, ResizeEdge, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE,
},
xdg_wm_base::{self, XdgWmBase},
},
wayland_server::{
@@ -30,14 +37,12 @@ use smithay::reexports::{
Weak as WlWeak,
},
};
use stardust_xr::schemas::flex::serialize;
use std::{
fmt::Debug,
sync::{Arc, Weak},
};
use tokio::sync::watch;
use tracing::{debug, warn};
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
impl GlobalDispatch<XdgWmBase, (), WaylandState> for WaylandState {
fn bind(
@@ -195,6 +200,14 @@ impl PositionerData {
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(
@@ -285,20 +298,6 @@ impl Dispatch<XdgPositioner, Mutex<PositionerData>, WaylandState> for WaylandSta
}
}
#[derive(Debug, Serialize, Clone, Copy)]
pub struct Geometry {
pub origin: Vector2<i32>,
pub size: Vector2<u32>,
}
impl Default for Geometry {
fn default() -> Self {
Self {
origin: Vector2::from([0; 2]),
size: Vector2::from([0; 2]),
}
}
}
pub struct XdgSurfaceData {
wl_surface: WlWeak<WlSurface>,
surface_id: SurfaceID,
@@ -388,6 +387,7 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
);
toplevel_data.lock().panel_item_node.replace(node);
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
handle_cursor(&panel_item, panel_item.backend.cursor.clone());
}
},
{
@@ -410,7 +410,12 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
xdg_surface.configure(SERIAL_COUNTER.inc());
return
};
panel_item.commit_toplevel();
let Some(xdg_surface) = toplevel_data.lock().xdg_surface() else {return};
let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return};
let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {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);
}
},
);
@@ -441,9 +446,17 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
parent_data.surface_id.clone(),
positioner,
));
let panel_item = parent_data.panel_item().unwrap();
handle_cursor(
&panel_item,
panel_item
.backend
.seat
.new_surface(&popup_data.lock().wl_surface().unwrap()),
);
let xdg_popup = data_init.init(id, popup_data);
xdg_surface_data.lock().surface_id = SurfaceID::Child(uid);
let panel_item = parent_data.panel_item().unwrap();
xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item);
debug!(?xdg_popup, ?xdg_surface, "Create XDG popup");
@@ -456,7 +469,6 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
let xdg_popup = xdg_popup.upgrade().unwrap();
let Some(popup_data) = PopupData::get(&xdg_popup) else {return};
let popup_data = popup_data.lock();
// panel_item.commit_popup(popup_data);
panel_item
.backend
.new_popup(&panel_item, &xdg_popup, &*popup_data);
@@ -498,10 +510,6 @@ impl Dispatch<XdgSurface, Mutex<XdgSurfaceData>, WaylandState> for WaylandState
}
}
fn serde_error<S: Serializer>(msg: &str) -> Result<S::Ok, S::Error> {
Err(serde::ser::Error::custom(msg))
}
#[derive(Debug, Clone)]
pub struct ToplevelData {
panel_item_node: Option<Arc<Node>>,
@@ -511,7 +519,6 @@ pub struct ToplevelData {
app_id: Option<String>,
max_size: Option<Vector2<u32>>,
min_size: Option<Vector2<u32>>,
states: Vec<u32>,
}
impl ToplevelData {
fn new(xdg_surface: &XdgSurface) -> Self {
@@ -523,7 +530,6 @@ impl ToplevelData {
app_id: None,
max_size: None,
min_size: None,
states: Vec::new(),
}
}
@@ -540,41 +546,6 @@ impl ToplevelData {
xdg_surface_data.panel_item()
}
}
impl Serialize for ToplevelData {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let Some(xdg_surface) = self.xdg_surface() else {return serializer.serialize_none()};
let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return serializer.serialize_none()};
let xdg_surface_data = xdg_surface_data.lock();
let geometry = xdg_surface_data.geometry.clone();
let Some(wl_surface) = xdg_surface_data.wl_surface() else {return serde_error::<S>("Wayland surface not found")};
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return serde_error::<S>("Core surface not found")};
let Some(size) = core_surface.size() else {return serializer.serialize_none()};
let geometry = geometry.unwrap_or_else(|| Geometry {
origin: [0; 2].into(),
size,
});
let mut seq = serializer.serialize_seq(None)?;
// Parent UID
seq.serialize_element(&self.parent.as_ref().and_then(|p| {
Some(
ToplevelData::get(&p.upgrade().ok()?)
.lock()
.panel_item()?
.uid
.clone(),
)
}))?;
seq.serialize_element(&self.title)?;
seq.serialize_element(&self.app_id)?;
seq.serialize_element(&size)?;
seq.serialize_element(&self.min_size)?;
seq.serialize_element(&self.max_size)?;
seq.serialize_element(&geometry)?;
seq.serialize_element(&self.states)?;
seq.end()
}
}
impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
fn request(
_state: &mut WaylandState,
@@ -588,30 +559,41 @@ impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
match request {
xdg_toplevel::Request::SetParent { parent } => {
debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent");
data.lock().parent = parent.map(|toplevel| toplevel.downgrade());
data.lock().parent = parent.clone().map(|toplevel| toplevel.downgrade());
let Some(panel_item) = data.lock().panel_item() else {return};
if let Some(parent) = parent {
panel_item.toplevel_parent_changed(
&ToplevelData::get(&parent).lock().panel_item().unwrap().uid,
);
}
}
xdg_toplevel::Request::SetTitle { title } => {
debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title");
data.lock().title = (!title.is_empty()).then_some(title);
data.lock().title = (!title.is_empty()).then_some(title.clone());
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.toplevel_title_changed(&title);
}
xdg_toplevel::Request::SetAppId { app_id } => {
debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID");
data.lock().app_id = (!app_id.is_empty()).then_some(app_id);
data.lock().app_id = (!app_id.is_empty()).then_some(app_id.clone());
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.toplevel_app_id_changed(&app_id);
}
xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
debug!(
?xdg_toplevel,
?seat,
serial,
x,
y,
"Show XDG Toplevel window menu"
);
xdg_toplevel::Request::ShowWindowMenu {
seat: _,
serial: _,
x,
y,
} => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.toplevel_window_menu([x, y].into());
}
xdg_toplevel::Request::Move { seat, serial } => {
debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request");
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Move);
panel_item.toplevel_move_request();
}
xdg_toplevel::Request::Resize {
seat,
@@ -627,7 +609,18 @@ impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
"XDG Toplevel resize request"
);
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Resize(edges as u32));
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),
};
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");
@@ -641,28 +634,28 @@ impl Dispatch<XdgToplevel, Mutex<ToplevelData>, WaylandState> for WaylandState {
}
xdg_toplevel::Request::SetMaximized => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Maximize(true));
panel_item.recommend_toplevel_state(ToplevelState::Maximized);
}
xdg_toplevel::Request::UnsetMaximized => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Maximize(false));
panel_item.recommend_toplevel_state(ToplevelState::UnMaximized);
}
xdg_toplevel::Request::SetFullscreen { output: _ } => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
panel_item.recommend_toplevel_state(ToplevelState::Fullscreen);
}
xdg_toplevel::Request::UnsetFullscreen => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
panel_item.recommend_toplevel_state(ToplevelState::UnFullscreen);
}
xdg_toplevel::Request::SetMinimized => {
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
panel_item.recommend_toplevel_state(ToplevelState::Minimized);
}
xdg_toplevel::Request::Destroy => {
debug!(?xdg_toplevel, "Destroy XDG Toplevel");
let Some(panel_item) = data.lock().panel_item() else {return};
panel_item.backend.on_drop();
panel_item.drop_toplevel();
}
_ => unreachable!(),
}
@@ -723,17 +716,6 @@ impl PopupData {
}
}
impl Serialize for PopupData {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let Some(positioner_data) = self.positioner_data() else {return serde_error::<S>("Positioner not found")};
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&self.uid)?;
seq.serialize_element(&self.parent_id)?;
seq.serialize_element(&positioner_data.get_pos())?;
seq.serialize_element(&positioner_data.size)?;
seq.end()
}
}
impl Debug for PopupData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("XdgPopupData")
@@ -793,12 +775,27 @@ impl Dispatch<XdgPopup, Mutex<PopupData>, WaylandState> for WaylandState {
}
}
struct XdgToplevelState {
fullscreen: bool,
maximized: bool,
activated: bool,
tiled_up: bool,
tiled_down: bool,
tiled_left: bool,
tiled_right: bool,
capability_window_menu: bool,
capability_maximize: bool,
capability_fullscreen: bool,
capability_minimize: bool,
}
pub struct XDGBackend {
toplevel: WlWeak<XdgToplevel>,
toplevel_wl_surface: WlWeak<WlSurface>,
toplevel_state: Mutex<XdgToplevelState>,
popups: Mutex<FxHashMap<String, WlWeak<XdgPopup>>>,
cursor: watch::Receiver<Option<CursorInfo>>,
pub seat: Arc<SeatData>,
seat: Arc<SeatData>,
pointer_grab: Mutex<Option<SurfaceID>>,
keyboard_grab: Mutex<Option<SurfaceID>>,
}
@@ -814,6 +811,19 @@ impl XDGBackend {
Some(XDGBackend {
toplevel: toplevel.downgrade(),
toplevel_wl_surface,
toplevel_state: Mutex::new(XdgToplevelState {
fullscreen: false,
maximized: false,
activated: false,
tiled_up: false,
tiled_down: false,
tiled_left: false,
tiled_right: false,
capability_window_menu: false,
capability_maximize: false,
capability_fullscreen: false,
capability_minimize: false,
}),
popups: Mutex::new(FxHashMap::default()),
cursor,
seat,
@@ -844,19 +854,50 @@ impl XDGBackend {
fn toplevel_wl_surface(&self) -> Option<WlSurface> {
self.toplevel_wl_surface.upgrade().ok()
}
fn input_surfaces(&self) -> Vec<WlSurface> {
let mut surfaces = self
.toplevel_wl_surface()
.map(|s| vec![s])
.unwrap_or_default();
surfaces.extend(self.popups.lock().values().filter_map(|p| {
let popup = p.upgrade().ok()?;
let popup_data = PopupData::get(&popup)?.lock();
let xdg_surface = popup_data.xdg_surface()?;
let xdg_surface_data = XdgSurfaceData::get(&xdg_surface)?.lock();
xdg_surface_data.wl_surface()
}));
surfaces
fn configure(&self, size: Option<Vector2<u32>>) {
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
let Some(wl_surface) = self.toplevel_wl_surface() 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.configure(SERIAL_COUNTER.inc());
self.flush_client();
}
fn states(&self) -> Vec<u32> {
let mut states = Vec::new();
let toplevel_state = self.toplevel_state.lock();
if toplevel_state.maximized {
states.push(1);
}
if toplevel_state.fullscreen {
states.push(2);
}
if toplevel_state.activated {
states.push(4);
}
if toplevel_state.tiled_left {
states.push(5);
}
if toplevel_state.tiled_right {
states.push(6);
}
if toplevel_state.tiled_up {
states.push(7);
}
if toplevel_state.tiled_down {
states.push(8);
}
states
}
pub fn new_popup(
@@ -869,53 +910,46 @@ impl XDGBackend {
self.popups.lock().insert(uid.clone(), popup.downgrade());
let Some(node) = panel_item.node() else {return};
let Ok(message) = serialize(&(&uid, data)) else {return};
let _ = node.send_remote_signal("new_child", message);
let Some(positioner_data) = data.positioner_data() else {return};
panel_item.new_child(ChildInfo {
uid,
parent: data.parent_id.clone(),
geometry: positioner_data.into(),
})
}
pub fn reposition_popup(&self, panel_item: &PanelItem<XDGBackend>, popup_state: &PopupData) {
let Some(node) = panel_item.node() else {return};
let Some(positioner_data) = popup_state.positioner_data() else {return};
let _ = node.send_remote_signal(
"reposition_child",
serialize((positioner_data.get_pos(), positioner_data.size)).unwrap(),
);
panel_item.reposition_child(&popup_state.uid, positioner_data.into())
}
pub fn drop_popup(&self, panel_item: &PanelItem<XDGBackend>, uid: &str) {
'seat_drop: {
let Some(popup) = self
panel_item.drop_child(uid);
let Some(popup) = self
.popups
.lock()
.remove(uid) else {break 'seat_drop};
let Some(popup) = popup.upgrade().ok() else {break 'seat_drop};
let Some(popup) = popup.data::<Arc<PopupData>>().cloned() else {break 'seat_drop};
let Some(wl_surface) = popup.wl_surface() else {break 'seat_drop};
self.seat.drop_surface(&wl_surface);
}
let Some(node) = panel_item.node() else {return};
let Ok(message) = serialize(uid) else {return};
let _ = node.send_remote_signal("drop_child", message);
.remove(uid) else {return};
let Some(popup) = popup.upgrade().ok() else {return};
let Some(popup) = popup.data::<Arc<PopupData>>().cloned() else {return};
let Some(wl_surface) = popup.wl_surface() else {return};
self.seat.drop_surface(&wl_surface);
}
fn popups_data(&self) -> Vec<PopupData> {
fn child_data(&self) -> Vec<ChildInfo> {
self.popups
.lock()
.values()
.filter_map(|v| Some(v.upgrade().ok()?.data::<Mutex<PopupData>>()?.lock().clone()))
.filter_map(|v| {
let popup = v.upgrade().ok()?;
let data = PopupData::get(&popup)?;
let data_lock = data.lock();
Some(ChildInfo {
uid: data_lock.uid.clone(),
parent: data_lock.parent_id.clone(),
geometry: data_lock.positioner_data()?.into(),
})
})
.collect::<Vec<_>>()
}
pub fn on_drop(&self) {
let Some(toplevel_popup) = self.toplevel_xdg_surface().and_then(|s| XdgSurfaceData::get(&s).and_then(|s| s.lock().panel_item.upgrade())) else {return};
toplevel_popup.drop_toplevel();
let Some(toplevel) = self.toplevel_wl_surface() else {return};
self.seat.drop_surface(&toplevel);
debug!("Dropped panel item gracefully");
}
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>() {
@@ -923,73 +957,54 @@ impl XDGBackend {
}
}
}
impl Drop for XDGBackend {
fn drop(&mut self) {
let Some(toplevel) = self.toplevel_wl_surface() else {return};
self.seat.drop_surface(&toplevel);
debug!("Dropped panel item gracefully");
}
}
impl Backend for XDGBackend {
fn serialize_start_data(&self, id: &str) -> Result<Message> {
let toplevel_state = self
fn start_data(&self) -> Result<PanelItemInitData> {
let toplevel_data = self
.toplevel()
.map(|t| ToplevelData::get(&t).lock().clone());
.map(|t| ToplevelData::get(&t).lock().clone())
.ok_or_else(|| eyre!("Could not get toplevel"))?;
let pointer_grab = self.pointer_grab.lock().clone();
let keyboard_grab = self.keyboard_grab.lock().clone();
Ok(serialize((
id,
(
self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()),
toplevel_state,
self.popups_data(),
pointer_grab,
keyboard_grab,
),
))?
.into())
}
fn serialize_toplevel(&self) -> Result<Message> {
let toplevel = self
.toplevel()
.ok_or_else(|| eyre!("Toplevel does not exist"))?;
let state = ToplevelData::get(&toplevel);
let data = serialize(&state.lock().clone())?;
Ok(data.into())
}
let Some(wl_surface) = self.toplevel_wl_surface() else {bail!("Wayland surface not found")};
let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {bail!("Core surface not found")};
let Some(size) = core_surface.size() else {bail!("Surface size not found")};
fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
let toplevel = ToplevelInfo {
parent: toplevel_data
.parent
.as_ref()
.and_then(|p| p.upgrade().ok())
.and_then(|p| ToplevelData::get(&p).lock().panel_item())
.map(|p| p.uid.clone()),
title: toplevel_data.title.clone(),
app_id: toplevel_data.app_id.clone(),
size,
min_size: toplevel_data.min_size.clone(),
max_size: toplevel_data.max_size.clone(),
logical_rectangle: XdgSurfaceData::get(&self.toplevel_xdg_surface().unwrap())
.unwrap()
.lock()
.geometry
.clone()
.unwrap(),
};
if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
return;
}
xdg_toplevel.wm_capabilities(capabilities);
xdg_surface.configure(SERIAL_COUNTER.inc());
self.flush_client();
}
fn configure_toplevel(
&self,
size: Option<Vector2<u32>>,
states: Vec<u32>,
bounds: Option<Vector2<u32>>,
) {
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
debug!(?size, ?states, ?bounds, "Configure toplevel info");
if let Some(bounds) = bounds {
if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE {
xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32);
}
}
let size = size.unwrap_or(Vector2::from([0; 2]));
xdg_toplevel.configure(
size.x as i32,
size.y as i32,
states
.into_iter()
.flat_map(|state| state.to_ne_bytes())
.collect(),
);
xdg_surface.configure(SERIAL_COUNTER.inc());
self.flush_client();
Ok(PanelItemInitData {
cursor: self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()),
toplevel,
children: self.child_data(),
pointer_grab,
keyboard_grab,
})
}
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
@@ -999,6 +1014,86 @@ impl Backend for XDGBackend {
core_surface.apply_material(model_part);
}
// fn set_toplevel_capabilities(&self, capabilities: Vec<u8>) {
// let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
// let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
// if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE {
// return;
// }
// xdg_toplevel.wm_capabilities(capabilities);
// xdg_surface.configure(SERIAL_COUNTER.inc());
// self.flush_client();
// }
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 toplevel_maximize(&self) {
self.toplevel_state.lock().maximized = true;
self.configure(None);
}
fn toplevel_unmaximize(&self) {
self.toplevel_state.lock().maximized = false;
self.configure(None);
}
fn toplevel_fullscreen(&self) {
self.toplevel_state.lock().fullscreen = true;
self.configure(None);
}
fn toplevel_unfullscreen(&self) {
self.toplevel_state.lock().fullscreen = false;
self.configure(None);
}
fn set_toplevel_focused_visuals(&self, focused: bool) {
self.toplevel_state.lock().activated = focused;
self.configure(None);
}
fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool) {
self.toplevel_state.lock().tiled_up = up;
self.toplevel_state.lock().tiled_down = down;
self.toplevel_state.lock().tiled_left = left;
self.toplevel_state.lock().tiled_right = right;
self.configure(None);
}
fn set_toplevel_bounds(&self, bounds: Option<Vector2<u32>>) {
let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return};
let Some(xdg_surface) = self.toplevel_xdg_surface() else {return};
if xdg_toplevel.version() <= EVT_CONFIGURE_BOUNDS_SINCE {
return;
}
xdg_toplevel.configure_bounds(
bounds.map(|b| b.x as i32).unwrap_or_default(),
bounds.map(|b| b.y as i32).unwrap_or_default(),
);
xdg_surface.configure(SERIAL_COUNTER.inc());
self.flush_client();
}
fn set_maximize_enabled(&self, enabled: bool) {
self.toplevel_state.lock().capability_maximize = enabled;
self.configure(None);
}
fn set_minimize_enabled(&self, enabled: bool) {
self.toplevel_state.lock().capability_minimize = enabled;
self.configure(None);
}
fn set_fullscreen_enabled(&self, enabled: bool) {
self.toplevel_state.lock().capability_fullscreen = enabled;
self.configure(None);
}
fn set_window_menu_enabled(&self, enabled: bool) {
self.toplevel_state.lock().capability_window_menu = enabled;
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
@@ -1030,14 +1125,6 @@ impl Backend for XDGBackend {
)
}
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
let context = xkb::Context::new(0);
let keymap =
Keymap::new_from_string(&context, keymap.to_string(), XKB_KEYMAP_FORMAT_TEXT_V1, 0)
.ok_or_else(|| eyre!("Keymap is not valid"))?;
self.seat.set_keymap(&keymap, self.input_surfaces());
Ok(())
}
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
let Some(surface) = self.wl_surface_from_id(surface) else {return};
self.seat.keyboard_event(

View File

@@ -1,13 +1,13 @@
use super::{
seat::{KeyboardEvent, PointerEvent, SeatData},
state::ClientState,
xdg_shell::PopupData,
};
use crate::{
nodes::{
drawable::model::ModelPart,
items::panel::{Backend, PanelItem, RecommendedState, SurfaceID},
Message,
items::panel::{
Backend, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, ToplevelState,
},
},
wayland::surface::CoreSurface,
};
@@ -18,8 +18,7 @@ use parking_lot::Mutex;
use smithay::{
reexports::{
calloop::{EventLoop, LoopSignal},
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, WEnum},
wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource},
x11rb::protocol::xproto::Window,
},
utils::{Logical, Rectangle},
@@ -29,7 +28,6 @@ use smithay::{
X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler,
},
};
use stardust_xr::schemas::flex::serialize;
use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration};
use tokio::sync::oneshot;
use tracing::debug;
@@ -165,7 +163,13 @@ impl XwmHandler for XWaylandHandler {
},
move |_| {
let Some(panel_item) = window.user_data().get::<Arc<PanelItem<X11Backend>>>() else {return};
panel_item.commit_toplevel();
panel_item.toplevel_size_changed(
[
window.geometry().size.w as u32,
window.geometry().size.h as u32,
]
.into(),
);
},
);
}
@@ -208,7 +212,7 @@ impl XwmHandler for XWaylandHandler {
fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) {
let Some(panel_item) = self.panel_item(&window) else {return};
debug!(?window, button, "X window requests move");
panel_item.recommend_toplevel_state(RecommendedState::Move);
panel_item.toplevel_move_request();
}
fn resize_request(
&mut self,
@@ -219,40 +223,39 @@ impl XwmHandler for XWaylandHandler {
) {
let Some(panel_item) = self.panel_item(&window) else {return};
debug!(?window, button, ?resize_edge, "X window requests resize");
panel_item.recommend_toplevel_state(RecommendedState::Resize(
WEnum::Value(match resize_edge {
ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom,
ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left,
ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft,
ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft,
ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right,
ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight,
ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight,
})
.into(),
));
let (up, down, left, right) = match resize_edge {
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, _xwm: XwmId, window: X11Surface) {
let Some(panel_item) = self.panel_item(&window) else {return};
panel_item.recommend_toplevel_state(RecommendedState::Maximize(true));
panel_item.recommend_toplevel_state(ToplevelState::Maximized);
}
fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
let Some(panel_item) = self.panel_item(&window) else {return};
panel_item.recommend_toplevel_state(RecommendedState::Maximize(false));
panel_item.recommend_toplevel_state(ToplevelState::UnMaximized);
}
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
let Some(panel_item) = self.panel_item(&window) else {return};
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
panel_item.recommend_toplevel_state(ToplevelState::Fullscreen);
}
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
let Some(panel_item) = self.panel_item(&window) else {return};
panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true));
panel_item.recommend_toplevel_state(ToplevelState::UnFullscreen);
}
fn minimize_request(&mut self, _xwm: XwmId, window: X11Surface) {
let Some(panel_item) = self.panel_item(&window) else {return};
panel_item.recommend_toplevel_state(RecommendedState::Minimize);
panel_item.recommend_toplevel_state(ToplevelState::Minimized);
}
}
@@ -280,62 +283,129 @@ impl X11Backend {
// }
}
impl Backend for X11Backend {
fn serialize_start_data(&self, id: &str) -> Result<Message> {
let size = (
self.toplevel.geometry().size.w as u32,
self.toplevel.geometry().size.h as u32,
);
let toplevel_state = (
None::<String>,
self.toplevel.title(),
None::<String>,
(
self.toplevel.geometry().size.w as u32,
self.toplevel.geometry().size.h as u32,
),
self.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)),
self.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)),
((0_i32, 0_i32), size),
vec![0_u32; 0],
);
let info = (
None::<(Vector2<u32>, Vector2<i32>)>,
toplevel_state,
Vec::<PopupData>::new(),
None::<SurfaceID>,
None::<SurfaceID>,
);
Ok(serialize((id, info))?.into())
}
fn serialize_toplevel(&self) -> Result<Message> {
let toplevel_state = (
None::<String>,
self.toplevel.title(),
None::<String>,
(
self.toplevel.geometry().size.w,
self.toplevel.geometry().size.h,
),
self.toplevel.min_size().map(|s| (s.w, s.h)),
self.toplevel.max_size().map(|s| (s.w, s.w)),
);
let data = serialize(&toplevel_state)?;
Ok(data.into())
}
// fn start_data(&self, id: &str) -> Result<Message> {
// let size = (
// self.toplevel.geometry().size.w as u32,
// self.toplevel.geometry().size.h as u32,
// );
// let toplevel_state = (
// None::<String>,
// self.toplevel.title(),
// None::<String>,
// (
// self.toplevel.geometry().size.w as u32,
// self.toplevel.geometry().size.h as u32,
// ),
// self.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)),
// self.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)),
// ((0_i32, 0_i32), size),
// vec![0_u32; 0],
// );
// let info = (
// None::<(Vector2<u32>, Vector2<i32>)>,
// toplevel_state,
// Vec::<PopupData>::new(),
// None::<SurfaceID>,
// None::<SurfaceID>,
// );
// Ok(serialize((id, info))?.into())
// }
// fn serialize_toplevel(&self) -> Result<Message> {
// let toplevel_state = (
// None::<String>,
// self.toplevel.title(),
// None::<String>,
// (
// self.toplevel.geometry().size.w,
// self.toplevel.geometry().size.h,
// ),
// self.toplevel.min_size().map(|s| (s.w, s.h)),
// self.toplevel.max_size().map(|s| (s.w, s.w)),
// );
// let data = serialize(&toplevel_state)?;
// Ok(data.into())
// }
fn set_toplevel_capabilities(&self, _capabilities: Vec<u8>) {}
// fn set_toplevel_capabilities(&self, _capabilities: Vec<u8>) {}
fn configure_toplevel(
&self,
size: Option<Vector2<u32>>,
states: Vec<u32>,
_bounds: Option<Vector2<u32>>,
) {
let _ = self.toplevel.configure(
size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))),
);
let _ = self.toplevel.set_maximized(states.contains(&1));
// fn set_toplevel_size(
// &self,
// size: Option<Vector2<u32>>,
// states: Vec<u32>,
// _bounds: Option<Vector2<u32>>,
// ) {
// let _ = self.toplevel.configure(
// size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))),
// );
// let _ = self.toplevel.set_maximized(states.contains(&1));
// }
fn start_data(&self) -> Result<PanelItemInitData> {
Ok(PanelItemInitData {
cursor: None,
toplevel: ToplevelInfo {
parent: None,
title: Some(self.toplevel.title()),
app_id: Some(self.toplevel.instance()),
size: [
self.toplevel.geometry().size.w as u32,
self.toplevel.geometry().size.h as u32,
]
.into(),
min_size: self
.toplevel
.min_size()
.map(|s| [s.w as u32, s.h as u32].into()),
max_size: self
.toplevel
.max_size()
.map(|s| [s.w as u32, s.h as u32].into()),
logical_rectangle: Geometry {
origin: [0, 0].into(),
size: [
self.toplevel.geometry().size.w as u32,
self.toplevel.geometry().size.h as u32,
]
.into(),
},
},
children: vec![],
pointer_grab: self._pointer_grab.lock().clone(),
keyboard_grab: self._keyboard_grab.lock().clone(),
})
}
fn close_toplevel(&self) {}
fn auto_size_toplevel(&self) {
let _ = self.toplevel.configure(None);
}
fn set_toplevel_size(&self, size: Vector2<u32>) {
let _ = self.toplevel.configure(Some(Rectangle {
loc: self.toplevel.geometry().loc,
size: (size.x as i32, size.y as i32).into(),
}));
}
fn toplevel_maximize(&self) {
let _ = self.toplevel.set_maximized(true);
}
fn toplevel_unmaximize(&self) {
let _ = self.toplevel.set_maximized(false);
}
fn toplevel_fullscreen(&self) {
let _ = self.toplevel.set_fullscreen(true);
}
fn toplevel_unfullscreen(&self) {
let _ = self.toplevel.set_fullscreen(false);
}
fn set_toplevel_tiling(&self, _up: bool, _down: bool, _left: bool, _right: bool) {}
fn set_toplevel_bounds(&self, _bounds: Option<Vector2<u32>>) {}
fn set_toplevel_focused_visuals(&self, focused: bool) {
let _ = self.toplevel.set_activated(focused);
}
fn set_maximize_enabled(&self, _enabled: bool) {}
fn set_minimize_enabled(&self, _enabled: bool) {}
fn set_fullscreen_enabled(&self, _enabled: bool) {}
fn set_window_menu_enabled(&self, _enabled: bool) {}
fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc<ModelPart>) {
let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return};
@@ -375,16 +445,6 @@ impl Backend for X11Backend {
)
}
fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> {
self.seat.set_keymap_str(
&keymap,
if let Some(toplevel) = self.toplevel.wl_surface() {
vec![toplevel]
} else {
vec![]
},
)
}
fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) {
let Some(surface) = self.wl_surface_from_id(surface) else {return};
self.seat.keyboard_event(