diff --git a/layershellev/src/events.rs b/layershellev/src/events.rs index 3a6927d..628896b 100644 --- a/layershellev/src/events.rs +++ b/layershellev/src/events.rs @@ -61,6 +61,13 @@ pub struct NewLayerShellSettings { pub keyboard_interactivity: KeyboardInteractivity, } +#[derive(Debug, PartialEq, Eq)] +pub struct NewPopUpSettings { + pub size: (u32, u32), + pub position: (i32, i32), + pub id: id::Id, +} + impl Default for NewLayerShellSettings { fn default() -> Self { NewLayerShellSettings { @@ -95,6 +102,7 @@ pub enum ReturnData { RedrawIndexRequest(Id), RequestSetCursorShape((String, WlPointer, u32)), NewLayerShell((NewLayerShellSettings, Option)), + NewPopUp((NewPopUpSettings, Option)), RemoveLayershell(id::Id), None, } diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index b6fb191..6bf09cc 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -112,6 +112,7 @@ //! ``` //! pub use events::NewLayerShellSettings; +use events::NewPopUpSettings; use sctk::reexports::calloop::LoopHandle; pub use waycrate_xkbkeycode::keyboard; pub use waycrate_xkbkeycode::xkb_keyboard; @@ -149,6 +150,7 @@ use wayland_client::{ }; use sctk::reexports::{calloop::EventLoop, calloop_wayland_source::WaylandSource}; +use wayland_protocols::xdg::shell::client::xdg_surface; use std::time::Duration; @@ -158,6 +160,12 @@ use wayland_protocols_wlr::layer_shell::v1::client::{ zwlr_layer_surface_v1::{self, Anchor, ZwlrLayerSurfaceV1}, }; +use wayland_protocols::xdg::shell::client::{ + xdg_popup::{self, XdgPopup}, + xdg_positioner::XdgPositioner, + xdg_wm_base::XdgWmBase, +}; + use wayland_protocols::{ wp::fractional_scale::v1::client::{ wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, @@ -282,6 +290,44 @@ impl ZxdgOutputInfo { /// /// and it can set a binding, you to store the related data. like /// a cario_context, which is binding to the buffer on the wl_surface. + +#[derive(Debug)] +enum Shell { + LayerShell(ZwlrLayerSurfaceV1), + PopUp(XdgPopup), +} + +impl PartialEq for Shell { + fn eq(&self, other: &ZwlrLayerSurfaceV1) -> bool { + match self { + Self::LayerShell(shell) => shell == other, + _ => false, + } + } +} + +impl PartialEq for Shell { + fn eq(&self, other: &XdgPopup) -> bool { + match self { + Self::PopUp(popup) => popup == other, + _ => false, + } + } +} + +impl Shell { + fn destroy(&self) { + match self { + Self::PopUp(popup) => popup.destroy(), + Self::LayerShell(shell) => shell.destroy(), + } + } + + fn is_popup(&self) -> bool { + matches!(self, Self::PopUp(_)) + } +} + #[derive(Debug)] pub struct WindowStateUnit { id: id::Id, @@ -289,7 +335,7 @@ pub struct WindowStateUnit { wl_surface: WlSurface, size: (u32, u32), buffer: Option, - layer_shell: ZwlrLayerSurfaceV1, + shell: Shell, zxdgoutput: Option, fractional_scale: Option, wl_output: Option, @@ -297,6 +343,12 @@ pub struct WindowStateUnit { becreated: bool, } +impl WindowStateUnit { + fn is_popup(&self) -> bool { + self.shell.is_popup() + } +} + impl WindowStateUnit { pub fn id(&self) -> id::Id { self.id @@ -385,31 +437,41 @@ impl WindowStateUnit { /// set the anchor of the current unit. please take the simple.rs as reference pub fn set_anchor(&self, anchor: Anchor) { - self.layer_shell.set_anchor(anchor); - self.wl_surface.commit(); + if let Shell::LayerShell(layer_shell) = &self.shell { + layer_shell.set_anchor(anchor); + self.wl_surface.commit(); + } } /// you can reset the margin which bind to the surface pub fn set_margin(&self, (top, right, bottom, left): (i32, i32, i32, i32)) { - self.layer_shell.set_margin(top, right, bottom, left); - self.wl_surface.commit(); + if let Shell::LayerShell(layer_shell) = &self.shell { + layer_shell.set_margin(top, right, bottom, left); + self.wl_surface.commit(); + } } pub fn set_layer(&self, layer: Layer) { - self.layer_shell.set_layer(layer); - self.wl_surface.commit(); + if let Shell::LayerShell(layer_shell) = &self.shell { + layer_shell.set_layer(layer); + self.wl_surface.commit(); + } } /// set the layer size of current unit pub fn set_size(&self, (width, height): (u32, u32)) { - self.layer_shell.set_size(width, height); - self.wl_surface.commit(); + if let Shell::LayerShell(layer_shell) = &self.shell { + layer_shell.set_size(width, height); + self.wl_surface.commit(); + } } /// set current exclusive_zone pub fn set_exclusive_zone(&self, zone: i32) { - self.layer_shell.set_exclusive_zone(zone); - self.wl_surface.commit(); + if let Shell::LayerShell(layer_shell) = &self.shell { + layer_shell.set_exclusive_zone(zone); + self.wl_surface.commit(); + } } /// you can use this function to set a binding data. the message passed back contain @@ -456,6 +518,7 @@ pub struct WindowState { event_queue: Option>>, wl_compositor: Option, xdg_output_manager: Option, + wmbase: Option, shm: Option, cursor_manager: Option, fractional_scale_manager: Option, @@ -669,6 +732,7 @@ impl Default for WindowState { event_queue: None, wl_compositor: None, shm: None, + wmbase: None, cursor_manager: None, xdg_output_manager: None, globals: None, @@ -731,6 +795,13 @@ impl WindowState { .position(|unit| Some(&unit.wl_surface) == self.current_surface.as_ref()) } + pub fn current_surface_id(&self) -> Option { + self.units + .iter() + .find(|unit| Some(&unit.wl_surface) == self.current_surface.as_ref()) + .map(|unit| unit.id()) + } + fn get_pos_from_surface(&self, surface: &WlSurface) -> Option { self.units .iter() @@ -1078,6 +1149,20 @@ impl Dispatch for WindowState { } } +impl Dispatch for WindowState { + fn event( + _state: &mut Self, + surface: &xdg_surface::XdgSurface, + event: ::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + if let xdg_surface::Event::Configure { serial } = event { + surface.ack_configure(serial); + } + } +} impl Dispatch for WindowState { fn event( state: &mut Self, @@ -1095,10 +1180,7 @@ impl Dispatch for WindowState< { surface.ack_configure(serial); - let Some(unit_index) = state - .units - .iter() - .position(|unit| unit.layer_shell == *surface) + let Some(unit_index) = state.units.iter().position(|unit| unit.shell == *surface) else { return; }; @@ -1112,6 +1194,33 @@ impl Dispatch for WindowState< } } +impl Dispatch for WindowState { + fn event( + state: &mut Self, + surface: &xdg_popup::XdgPopup, + event: ::Event, + _data: &(), + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + if let xdg_popup::Event::Configure { width, height, .. } = event { + let Some(unit_index) = state.units.iter().position(|unit| unit.shell == *surface) + else { + return; + }; + state.units[unit_index].size = (width as u32, height as u32); + + state.message.push(( + Some(unit_index), + DispatchMessageInner::RefreshSurface { + width: width as u32, + height: height as u32, + }, + )); + } + } +} + impl Dispatch for WindowState { fn event( state: &mut Self, @@ -1192,6 +1301,8 @@ delegate_noop!(@ WindowState: ignore ZwpVirtualKeyboardManagerV1); delegate_noop!(@ WindowState: ignore ZxdgOutputManagerV1); delegate_noop!(@ WindowState: ignore WpFractionalScaleManagerV1); +delegate_noop!(@ WindowState: ignore XdgPositioner); +delegate_noop!(@ WindowState: ignore XdgWmBase); impl WindowState { pub fn build(mut self) -> Result { @@ -1217,6 +1328,9 @@ impl WindowState { self.shm = Some(shm); self.seat = Some(globals.bind::(&qh, 1..=1, ())?); + let wmbase = globals.bind::(&qh, 2..=2, ())?; + self.wmbase = Some(wmbase); + let cursor_manager = globals .bind::(&qh, 1..=1, ()) .ok(); @@ -1285,7 +1399,7 @@ impl WindowState { wl_surface, size: (0, 0), buffer: None, - layer_shell: layer, + shell: Shell::LayerShell(layer), zxdgoutput: None, fractional_scale, binding: None, @@ -1340,7 +1454,7 @@ impl WindowState { wl_surface, size: (0, 0), buffer: None, - layer_shell: layer, + shell: Shell::LayerShell(layer), zxdgoutput: Some(ZxdgOutputInfo::new(zxdgoutput)), fractional_scale, binding: None, @@ -1419,6 +1533,7 @@ impl WindowState { let xdg_output_manager = self.xdg_output_manager.take().unwrap(); let connection = self.connection.take().unwrap(); let mut init_event = None; + let wmbase = self.wmbase.take().unwrap(); while !matches!(init_event, Some(ReturnData::None)) { match init_event { @@ -1542,7 +1657,7 @@ impl WindowState { wl_surface, size: (0, 0), buffer: None, - layer_shell: layer, + shell: Shell::LayerShell(layer), zxdgoutput: Some(ZxdgOutputInfo::new(zxdgoutput)), fractional_scale, binding: None, @@ -1806,7 +1921,7 @@ impl WindowState { wl_surface, size: (0, 0), buffer: None, - layer_shell: layer, + shell: Shell::LayerShell(layer), zxdgoutput: None, fractional_scale, becreated: true, @@ -1814,6 +1929,60 @@ impl WindowState { binding: info, }); } + ReturnData::NewPopUp(( + NewPopUpSettings { + size: (width, height), + position: (x, y), + id, + }, + info, + )) => { + let Some(index) = self + .units + .iter() + .position(|unit| !unit.is_popup() && unit.id == id) + else { + continue; + }; + let positioner = wmbase.create_positioner(&qh, ()); + positioner.set_size(width as i32, height as i32); + positioner.set_anchor_rect(x, y, width as i32, height as i32); + let wl_xdg_surface = + wmbase.get_xdg_surface(&self.units[index].wl_surface, &qh, ()); + let popup = wl_xdg_surface.get_popup(None, &positioner, &qh, ()); + + let Shell::LayerShell(shell) = &self.units[index].shell else { + unreachable!() + }; + shell.get_popup(&popup); + + let wl_surface = wmcompositer.create_surface(&qh, ()); // and create a surface. if two or more, + + let mut fractional_scale = None; + if let Some(ref fractional_scale_manager) = fractional_scale_manager { + fractional_scale = + Some(fractional_scale_manager.get_fractional_scale( + &wl_surface, + &qh, + (), + )); + } + self.units[index].wl_surface.commit(); + wl_surface.commit(); + self.units.push(WindowStateUnit { + id: id::Id::unique(), + display: connection.display(), + wl_surface, + size: (0, 0), + buffer: None, + shell: Shell::PopUp(popup), + zxdgoutput: None, + fractional_scale, + becreated: true, + wl_output: None, + binding: info, + }); + } ReturnData::RemoveLayershell(id) => { let Some(index) = self .units @@ -1822,7 +1991,7 @@ impl WindowState { else { continue; }; - self.units[index].layer_shell.destroy(); + self.units[index].shell.destroy(); self.units[index].wl_surface.destroy(); if let Some(buffer) = self.units[index].buffer.as_ref() { buffer.destroy()