Fix shell focus, launcher touch dismissal, and sandbox clipboard access
This commit is contained in:
1 parent
c8dfb3074d
commit
a62f1367bd
8 files changed
+126
-79
No files matched your search
@@ -6,6 +6,7 @@ use smithay::reexports::wayland_server::{Resource, backend::ClientId};
|
|||||||
use smithay::utils::Serial;
|
use smithay::utils::Serial;
|
||||||
|
|
||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
|
#[cfg(test)]
|
||||||
use smithay::utils::SERIAL_COUNTER;
|
use smithay::utils::SERIAL_COUNTER;
|
||||||
use smithay::wayland::xdg_activation::{
|
use smithay::wayland::xdg_activation::{
|
||||||
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
|
XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
|
||||||
@@ -110,34 +111,10 @@ impl XdgActivationHandler for Ferese {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(window) = self
|
let Some(id) = self.windows.id_for_surface(&surface) else {
|
||||||
.space
|
|
||||||
.elements()
|
|
||||||
.find(|window| {
|
|
||||||
window
|
|
||||||
.toplevel()
|
|
||||||
.is_some_and(|toplevel| toplevel.wl_surface() == &surface)
|
|
||||||
})
|
|
||||||
.cloned()
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
self.activate_managed_window(id);
|
||||||
self.focused_window = self.windows.ids().get(&window).copied();
|
|
||||||
if let Some(focused) = self.focused_window
|
|
||||||
&& let Err(error) = self.workspaces.focus_window(focused)
|
|
||||||
{
|
|
||||||
tracing::error!(%error, ?focused, "failed to update workspace focus");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.raise_window(&window, true);
|
|
||||||
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
|
||||||
keyboard.set_focus(self, Some(surface), SERIAL_COUNTER.next_serial());
|
|
||||||
self.space.elements().for_each(|window| {
|
|
||||||
if let Some(toplevel) = window.toplevel() {
|
|
||||||
toplevel.send_pending_configure();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,8 +229,7 @@ mod tests {
|
|||||||
assert_ne!(approved.client_id, Some(recipient.id()));
|
assert_ne!(approved.client_id, Some(recipient.id()));
|
||||||
assert!(approved.user_data.get::<ApprovedInteraction>().unwrap().0.elapsed() < TOKEN_MAX_AGE);
|
assert!(approved.user_data.get::<ApprovedInteraction>().unwrap().0.elapsed() < TOKEN_MAX_AGE);
|
||||||
|
|
||||||
// Exercise the production handler with a real recipient toplevel.
|
// A valid handoff must not focus a recipient before its first buffer.
|
||||||
// Creating the window needs no GPU or client buffer.
|
|
||||||
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_wm_base::XdgWmBase;
|
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_wm_base::XdgWmBase;
|
||||||
use smithay::reexports::wayland_server::protocol::wl_compositor::WlCompositor;
|
use smithay::reexports::wayland_server::protocol::wl_compositor::WlCompositor;
|
||||||
use smithay::wayland::shell::xdg::XdgWmBaseUserData;
|
use smithay::wayland::shell::xdg::XdgWmBaseUserData;
|
||||||
@@ -279,7 +255,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.expect("recipient's toplevel");
|
.expect("recipient's toplevel");
|
||||||
state.request_activation(token, approved, target.clone());
|
state.request_activation(token, approved, target.clone());
|
||||||
assert_eq!(state.seat.get_keyboard().unwrap().current_focus(), Some(target));
|
assert_eq!(state.seat.get_keyboard().unwrap().current_focus(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -195,11 +195,14 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Ferese {
|
impl Ferese {
|
||||||
|
pub(crate) fn layer_keyboard_eligible(&self, layer: &DesktopLayerSurface) -> bool {
|
||||||
|
layer.can_receive_keyboard_focus() && self.layer_mapped_and_visible(layer)
|
||||||
|
}
|
||||||
|
|
||||||
/// Layer-map membership tracks the role and output, not buffer mapping.
|
/// Layer-map membership tracks the role and output, not buffer mapping.
|
||||||
/// Use the renderer state updated by on_commit_buffer_handler, as for windows.
|
/// Use the renderer state updated by on_commit_buffer_handler, as for windows.
|
||||||
pub(crate) fn layer_keyboard_eligible(&self, layer: &DesktopLayerSurface) -> bool {
|
fn layer_mapped_and_visible(&self, layer: &DesktopLayerSurface) -> bool {
|
||||||
layer.layer_surface().alive()
|
layer.layer_surface().alive()
|
||||||
&& layer.can_receive_keyboard_focus()
|
|
||||||
&& smithay::backend::renderer::utils::with_renderer_surface_state(layer.wl_surface(), |state| {
|
&& smithay::backend::renderer::utils::with_renderer_surface_state(layer.wl_surface(), |state| {
|
||||||
state.buffer().is_some()
|
state.buffer().is_some()
|
||||||
})
|
})
|
||||||
@@ -262,10 +265,16 @@ impl Ferese {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
if let Some(root) = self.keyboard_layer_root().filter(|_| {
|
if let Some(root) = self.keyboard_layer_root().filter(|root| {
|
||||||
!self
|
!self.keyboard_focused_layer().is_some_and(|layer| {
|
||||||
.keyboard_focused_layer()
|
// A popup owns its keyboard focus independently of the bar's
|
||||||
.is_some_and(|layer| self.layer_keyboard_eligible(&layer))
|
// interactivity policy, but still needs a visible, mapped parent.
|
||||||
|
if keyboard.current_focus().as_ref() != Some(root) {
|
||||||
|
self.layer_mapped_and_visible(&layer)
|
||||||
|
} else {
|
||||||
|
self.layer_keyboard_eligible(&layer)
|
||||||
|
}
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
// A popup grab cannot keep an ineligible layer alive as input owner.
|
// A popup grab cannot keep an ineligible layer alive as input owner.
|
||||||
for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(&root) {
|
for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(&root) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod layer_shell;
|
|||||||
pub(crate) mod screencopy;
|
pub(crate) mod screencopy;
|
||||||
pub(crate) mod screenshot;
|
pub(crate) mod screenshot;
|
||||||
pub(crate) mod screenshot_worker;
|
pub(crate) mod screenshot_worker;
|
||||||
|
pub(crate) mod security_context;
|
||||||
pub(crate) mod window_capture;
|
pub(crate) mod window_capture;
|
||||||
mod xdg_shell;
|
mod xdg_shell;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use smithay::reexports::wayland_server::Client;
|
||||||
|
use smithay::wayland::security_context::{SecurityContext, SecurityContextHandler, SecurityContextListenerSource};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
use crate::state::ClientState;
|
||||||
|
|
||||||
|
pub(crate) fn unrestricted_client(client: &Client) -> bool {
|
||||||
|
client
|
||||||
|
.get_data::<ClientState>()
|
||||||
|
.is_some_and(|state| state.security_context.is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecurityContextHandler for Ferese {
|
||||||
|
fn context_created(&mut self, source: SecurityContextListenerSource, context: SecurityContext) {
|
||||||
|
if let Err(error) = self.loop_handle.insert_source(source, move |stream, _, state| {
|
||||||
|
let data = ClientState {
|
||||||
|
security_context: Some(context.clone()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Err(error) = state.display_handle.insert_client(stream, Arc::new(data)) {
|
||||||
|
tracing::warn!(%error, "failed to register sandboxed Wayland client");
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
tracing::warn!(%error, "failed to register security context listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smithay::delegate_security_context!(Ferese);
|
||||||
+41
-14
@@ -629,23 +629,10 @@ impl Ferese {
|
|||||||
|
|
||||||
if event.state() == ButtonState::Pressed
|
if event.state() == ButtonState::Pressed
|
||||||
&& !pointer.is_grabbed()
|
&& !pointer.is_grabbed()
|
||||||
&& let Some(surface) = self.seat.get_keyboard().and_then(|keyboard| keyboard.current_focus())
|
&& self.dismiss_launcher_outside(pointer.current_location())
|
||||||
&& self
|
|
||||||
.space
|
|
||||||
.layer_for_surface(&surface, smithay::desktop::WindowSurfaceType::ALL)
|
|
||||||
.is_some_and(|layer| layer.namespace() == "ferese-launcher")
|
|
||||||
&& !self
|
|
||||||
.layer_under(pointer.current_location())
|
|
||||||
.is_some_and(|(layer, _, _)| layer.wl_surface() == &surface)
|
|
||||||
&& let Some(shell) = self
|
|
||||||
.shell_resources
|
|
||||||
.iter()
|
|
||||||
.filter_map(|shell| shell.upgrade().ok())
|
|
||||||
.find(|shell| shell.version() >= 10)
|
|
||||||
{
|
{
|
||||||
// Consume both halves of the click before underlying windows or tabs see it.
|
// Consume both halves of the click before underlying windows or tabs see it.
|
||||||
self.consumed_pointer_buttons.insert(event.button_code());
|
self.consumed_pointer_buttons.insert(event.button_code());
|
||||||
shell.toggle_launcher(String::new());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,6 +810,10 @@ impl Ferese {
|
|||||||
let Some(location) = self.absolute_event_position(&event) else {
|
let Some(location) = self.absolute_event_position(&event) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if !self.consumed_touch_slots.is_empty() || self.dismiss_launcher_outside(location) {
|
||||||
|
self.consumed_touch_slots.insert(event.slot());
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.focus_window_at(location, SERIAL_COUNTER.next_serial(), true);
|
self.focus_window_at(location, SERIAL_COUNTER.next_serial(), true);
|
||||||
let touch = self.seat.get_touch().expect("seat has touch capability");
|
let touch = self.seat.get_touch().expect("seat has touch capability");
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
@@ -843,6 +834,9 @@ impl Ferese {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
InputEvent::TouchMotion { event, .. } => {
|
InputEvent::TouchMotion { event, .. } => {
|
||||||
|
if self.consumed_touch_slots.contains(&event.slot()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let Some(location) = self.absolute_event_position(&event) else {
|
let Some(location) = self.absolute_event_position(&event) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -859,6 +853,9 @@ impl Ferese {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
InputEvent::TouchUp { event, .. } => {
|
InputEvent::TouchUp { event, .. } => {
|
||||||
|
if self.consumed_touch_slots.remove(&event.slot()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let touch = self.seat.get_touch().expect("seat has touch capability");
|
let touch = self.seat.get_touch().expect("seat has touch capability");
|
||||||
touch.up(
|
touch.up(
|
||||||
self,
|
self,
|
||||||
@@ -870,6 +867,7 @@ impl Ferese {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
InputEvent::TouchCancel { .. } => {
|
InputEvent::TouchCancel { .. } => {
|
||||||
|
self.consumed_touch_slots.clear();
|
||||||
let touch = self.seat.get_touch().expect("seat has touch capability");
|
let touch = self.seat.get_touch().expect("seat has touch capability");
|
||||||
touch.cancel(self);
|
touch.cancel(self);
|
||||||
}
|
}
|
||||||
@@ -881,6 +879,35 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dismiss_launcher_outside(&self, location: Point<f64, Logical>) -> bool {
|
||||||
|
if self.session_lock.active() || self.input_capture.active() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(surface) = self.seat.get_keyboard().and_then(|keyboard| keyboard.current_focus()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if !self
|
||||||
|
.space
|
||||||
|
.layer_for_surface(&surface, smithay::desktop::WindowSurfaceType::ALL)
|
||||||
|
.is_some_and(|layer| layer.namespace() == "ferese-launcher")
|
||||||
|
|| self
|
||||||
|
.layer_under(location)
|
||||||
|
.is_some_and(|(layer, _, _)| layer.wl_surface() == &surface)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(shell) = self
|
||||||
|
.shell_resources
|
||||||
|
.iter()
|
||||||
|
.filter_map(|shell| shell.upgrade().ok())
|
||||||
|
.find(|shell| shell.version() >= 10)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
shell.toggle_launcher(String::new());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn start_floating_pointer_grab(&mut self, pointer: &PointerHandle<Self>, button: u32, serial: Serial) -> bool {
|
fn start_floating_pointer_grab(&mut self, pointer: &PointerHandle<Self>, button: u32, serial: Serial) -> bool {
|
||||||
if self.session_lock.active()
|
if self.session_lock.active()
|
||||||
|| pointer.is_grabbed()
|
|| pointer.is_grabbed()
|
||||||
|
|||||||
+21
-10
@@ -470,6 +470,7 @@ pub struct Ferese {
|
|||||||
pub(crate) tab_indicator_settings: crate::config::TabIndicatorSettings,
|
pub(crate) tab_indicator_settings: crate::config::TabIndicatorSettings,
|
||||||
pub(crate) tab_bars: HashMap<WorkspaceId, Vec<ferese_layout::TabBar>>,
|
pub(crate) tab_bars: HashMap<WorkspaceId, Vec<ferese_layout::TabBar>>,
|
||||||
pub(crate) consumed_pointer_buttons: HashSet<u32>,
|
pub(crate) consumed_pointer_buttons: HashSet<u32>,
|
||||||
|
pub(crate) consumed_touch_slots: HashSet<smithay::backend::input::TouchSlot>,
|
||||||
pub(crate) focus_history: ferese_core::FocusHistory,
|
pub(crate) focus_history: ferese_core::FocusHistory,
|
||||||
pub(crate) focus_cycle: Option<ferese_core::FocusCycle>,
|
pub(crate) focus_cycle: Option<ferese_core::FocusCycle>,
|
||||||
column_width_presets: Vec<ColumnWidth>,
|
column_width_presets: Vec<ColumnWidth>,
|
||||||
@@ -553,6 +554,7 @@ pub struct Ferese {
|
|||||||
pub primary_selection_state: PrimarySelectionState,
|
pub primary_selection_state: PrimarySelectionState,
|
||||||
pub wlr_data_control_state: wlr_data_control::DataControlState,
|
pub wlr_data_control_state: wlr_data_control::DataControlState,
|
||||||
pub ext_data_control_state: ext_data_control::DataControlState,
|
pub ext_data_control_state: ext_data_control::DataControlState,
|
||||||
|
pub security_context_state: smithay::wayland::security_context::SecurityContextState,
|
||||||
pub relative_pointer_state: RelativePointerManagerState,
|
pub relative_pointer_state: RelativePointerManagerState,
|
||||||
pub seat_state: SeatState<Self>,
|
pub seat_state: SeatState<Self>,
|
||||||
pub shm_state: ShmState,
|
pub shm_state: ShmState,
|
||||||
@@ -659,16 +661,22 @@ impl Ferese {
|
|||||||
let presentation_state = PresentationState::new::<Self>(&display_handle, libc::CLOCK_MONOTONIC as u32);
|
let presentation_state = PresentationState::new::<Self>(&display_handle, libc::CLOCK_MONOTONIC as u32);
|
||||||
let data_device_state = DataDeviceState::new::<Self>(&display_handle);
|
let data_device_state = DataDeviceState::new::<Self>(&display_handle);
|
||||||
let primary_selection_state = PrimarySelectionState::new::<Self>(&display_handle);
|
let primary_selection_state = PrimarySelectionState::new::<Self>(&display_handle);
|
||||||
// Clipboard tools need direct selection access. Without data control,
|
let security_context_state = smithay::wayland::security_context::SecurityContextState::new::<Self, _>(
|
||||||
// wl-copy maps a temporary window to obtain keyboard focus.
|
&display_handle,
|
||||||
let wlr_data_control_state =
|
crate::handlers::security_context::unrestricted_client,
|
||||||
wlr_data_control::DataControlState::new::<Self, _>(&display_handle, Some(&primary_selection_state), |_| {
|
);
|
||||||
true
|
// Host clipboard tools retain direct access. Sandboxed clients use
|
||||||
});
|
// ordinary selection protocols, which follow keyboard focus.
|
||||||
let ext_data_control_state =
|
let wlr_data_control_state = wlr_data_control::DataControlState::new::<Self, _>(
|
||||||
ext_data_control::DataControlState::new::<Self, _>(&display_handle, Some(&primary_selection_state), |_| {
|
&display_handle,
|
||||||
true
|
Some(&primary_selection_state),
|
||||||
});
|
crate::handlers::security_context::unrestricted_client,
|
||||||
|
);
|
||||||
|
let ext_data_control_state = ext_data_control::DataControlState::new::<Self, _>(
|
||||||
|
&display_handle,
|
||||||
|
Some(&primary_selection_state),
|
||||||
|
crate::handlers::security_context::unrestricted_client,
|
||||||
|
);
|
||||||
|
|
||||||
let relative_pointer_state = RelativePointerManagerState::new::<Self>(&display_handle);
|
let relative_pointer_state = RelativePointerManagerState::new::<Self>(&display_handle);
|
||||||
let viewporter_state = ViewporterState::new::<Self>(&display_handle);
|
let viewporter_state = ViewporterState::new::<Self>(&display_handle);
|
||||||
@@ -777,6 +785,7 @@ impl Ferese {
|
|||||||
tab_indicator_settings: config.tab_indicator,
|
tab_indicator_settings: config.tab_indicator,
|
||||||
tab_bars: HashMap::new(),
|
tab_bars: HashMap::new(),
|
||||||
consumed_pointer_buttons: HashSet::new(),
|
consumed_pointer_buttons: HashSet::new(),
|
||||||
|
consumed_touch_slots: HashSet::new(),
|
||||||
focus_history: Default::default(),
|
focus_history: Default::default(),
|
||||||
focus_cycle: None,
|
focus_cycle: None,
|
||||||
column_width_presets: config.column_width_presets,
|
column_width_presets: config.column_width_presets,
|
||||||
@@ -855,6 +864,7 @@ impl Ferese {
|
|||||||
primary_selection_state,
|
primary_selection_state,
|
||||||
wlr_data_control_state,
|
wlr_data_control_state,
|
||||||
ext_data_control_state,
|
ext_data_control_state,
|
||||||
|
security_context_state,
|
||||||
relative_pointer_state,
|
relative_pointer_state,
|
||||||
seat_state,
|
seat_state,
|
||||||
shm_state,
|
shm_state,
|
||||||
@@ -1264,6 +1274,7 @@ where
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub compositor_state: CompositorClientState,
|
pub compositor_state: CompositorClientState,
|
||||||
|
pub(crate) security_context: Option<smithay::wayland::security_context::SecurityContext>,
|
||||||
pub(crate) capabilities: crate::private_client::ClientCapabilities,
|
pub(crate) capabilities: crate::private_client::ClientCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,9 @@ impl Ferese {
|
|||||||
let Some(window) = self.windows.window(id).cloned() else {
|
let Some(window) = self.windows.window(id).cloned() else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
if !super::window_has_buffer(&window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if !activate_window_workspace(&mut self.workspaces, &mut self.output_workspaces, id) {
|
if !activate_window_workspace(&mut self.workspaces, &mut self.output_workspaces, id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -510,7 +510,6 @@ impl FereseShell {
|
|||||||
|
|
||||||
pub(super) fn attach_effects(&mut self, id: window::Id, surface: &wl_surface::WlSurface) {
|
pub(super) fn attach_effects(&mut self, id: window::Id, surface: &wl_surface::WlSurface) {
|
||||||
let hidden = self.bar_hidden(id);
|
let hidden = self.bar_hidden(id);
|
||||||
let islands = self.panel_for_bar(id).surface() == ferese_panel::PanelSurface::None;
|
|
||||||
let Some(entry) = self.outputs.iter_mut().find(|entry| entry.bar == id) else {
|
let Some(entry) = self.outputs.iter_mut().find(|entry| entry.bar == id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -521,12 +520,15 @@ impl FereseShell {
|
|||||||
|
|
||||||
EFFECT_FRAME_PENDING.store(false, Ordering::Relaxed);
|
EFFECT_FRAME_PENDING.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
let binding = if islands {
|
// The first panel presentation supplies the role and measured shape
|
||||||
// Wait for measured island bounds rather than briefly painting the full bar.
|
// together with its buffer. Enabling the role here would paint the
|
||||||
EffectsBinding::attach_role(&mut self.effects_context, surface, None, 1.0)
|
// whole surface before those bounds arrive.
|
||||||
} else {
|
let binding = EffectsBinding::attach_role(
|
||||||
EffectsBinding::attach(&mut self.effects_context, surface, !hidden)
|
&mut self.effects_context,
|
||||||
};
|
surface,
|
||||||
|
None,
|
||||||
|
if hidden { 0.0 } else { 1.0 },
|
||||||
|
);
|
||||||
|
|
||||||
match binding {
|
match binding {
|
||||||
Ok(binding) => entry.effects = Some(binding),
|
Ok(binding) => entry.effects = Some(binding),
|
||||||
@@ -644,19 +646,6 @@ pub(super) struct EffectsBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EffectsBinding {
|
impl EffectsBinding {
|
||||||
pub(super) fn attach(
|
|
||||||
context: &mut Option<std::rc::Rc<EffectsContext>>,
|
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
visible: bool,
|
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
|
||||||
Self::attach_role(
|
|
||||||
context,
|
|
||||||
surface,
|
|
||||||
visible.then_some(ferese_surface_effects_v1::Role::Panel),
|
|
||||||
if visible { 1.0 } else { 0.0 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn attach_role(
|
pub(super) fn attach_role(
|
||||||
context: &mut Option<std::rc::Rc<EffectsContext>>,
|
context: &mut Option<std::rc::Rc<EffectsContext>>,
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
|
|||||||
Reference in new issue
Block a user