Skip to content

Commit

Permalink
feat(sctk): more sctk features
Browse files Browse the repository at this point in the history
feat(sctk): support ShowWindowMenu

feat(sctk): support for overflow widget

sctk: Fixes for cursor icon

* With multiple windows, `SetCursor` is only sent for the focused
  window. Fixing a flicker between icons when two windows are using
  different cursors.
* If there is a drag surface, let that surface set the cursor. And not
  any other.
* Set cursor on `enter`, and when switching between CSDs and app area.

Fixes pop-os/libcosmic#533.

improv(sctk): per-surface cursor position tracking
  • Loading branch information
mmstick authored and ryanabx committed Jul 29, 2024
1 parent cc4a363 commit f9ce292
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 160 deletions.
30 changes: 26 additions & 4 deletions core/src/event/wayland/window.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
#![allow(missing_docs)]

use sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
use sctk::{
reexports::csd_frame::{WindowManagerCapabilities, WindowState},
shell::xdg::window::WindowConfigure,
};

/// window events
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone)]
pub enum WindowEvent {
/// window manager capabilities
/// Window manager capabilities.
WmCapabilities(WindowManagerCapabilities),
/// window state
/// Window state.
State(WindowState),
/// Window configure event.
Configure(WindowConfigure),
}

impl PartialEq for WindowEvent {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::WmCapabilities(a), Self::WmCapabilities(b)) => a == b,
(Self::State(a), Self::State(b)) => a == b,
(Self::Configure(a), Self::Configure(b)) => {
a.capabilities == b.capabilities
&& a.state == b.state
&& a.decoration_mode == b.decoration_mode
&& a.new_size == b.new_size
&& a.suggested_bounds == b.suggested_bounds
}
_ => false,
}
}
}
16 changes: 6 additions & 10 deletions runtime/src/command/platform_specific/wayland/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,6 @@ pub enum Action<T> {
ShowWindowMenu {
/// id of the window
id: Id,
/// x location of popup
x: i32,
/// y location of popup
y: i32,
},
/// Set the mode of the window
Mode(Id, Mode),
Expand Down Expand Up @@ -201,9 +197,7 @@ impl<T> Action<T> {
Action::Fullscreen { id } => Action::Fullscreen { id },
Action::UnsetFullscreen { id } => Action::UnsetFullscreen { id },
Action::InteractiveMove { id } => Action::InteractiveMove { id },
Action::ShowWindowMenu { id, x, y } => {
Action::ShowWindowMenu { id, x, y }
}
Action::ShowWindowMenu { id } => Action::ShowWindowMenu { id },
Action::InteractiveResize { id, edge } => {
Action::InteractiveResize { id, edge }
}
Expand Down Expand Up @@ -274,9 +268,9 @@ impl<T> fmt::Debug for Action<T> {
"Action::Window::InteractiveMove {{ id: {:?} }}",
id
),
Action::ShowWindowMenu { id, x, y } => write!(
Action::ShowWindowMenu { id } => write!(
f,
"Action::Window::ShowWindowMenu {{ id: {:?}, x: {x}, y: {y} }}",
"Action::Window::ShowWindowMenu {{ id: {:?} }}",
id
),
Action::InteractiveResize { id, edge } => write!(
Expand Down Expand Up @@ -372,11 +366,13 @@ impl<T> TryFrom<window::Action<T>> for Action<T> {
| window::Action::RequestUserAttention(_, _)
| window::Action::GainFocus(_)
| window::Action::ChangeLevel(_, _)
| window::Action::ShowWindowMenu(_)
| window::Action::FetchId(_, _)
| window::Action::ChangeIcon(_, _)
| window::Action::Screenshot(_, _)
| window::Action::FetchMinimized(_, _) => Err(Error::NotSupported),
window::Action::ShowWindowMenu(id) => {
Ok(Action::ShowWindowMenu { id })
}
window::Action::Maximize(id, maximized) => {
if maximized {
Ok(Action::Maximize { id })
Expand Down
35 changes: 24 additions & 11 deletions sctk/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,14 +535,20 @@ where
crate::sctk_event::WindowEventVariant::WmCapabilities(_)
| crate::sctk_event::WindowEventVariant::ConfigureBounds { .. } => {}
crate::sctk_event::WindowEventVariant::Configure(
configure,
current_size,
_,
wl_surface,
first,
) | crate::sctk_event::WindowEventVariant::Size(
current_size,
wl_surface,
first,
) => {
if let Some(id) = surface_ids.get(&wl_surface.id()) {
let Some(state) = states.get_mut(&id.inner()) else {
continue;
};
let (w, h) = auto_size_surfaces.get(id).map_or_else(|| (current_size.0.get(), current_size.1.get()), |(w, h, _, _)| (*w, *h));
if state.surface.is_none() {
let wrapper = SurfaceDisplayWrapper {
backend: backend.clone(),
Expand All @@ -553,16 +559,12 @@ where
simple_clipboard = unsafe {Clipboard::connect(&h)};
}
}
let mut c_surface = compositor.create_surface(wrapper.clone(), configure.new_size.0.unwrap().get(), configure.new_size.1.unwrap().get());
compositor.configure_surface(&mut c_surface, configure.new_size.0.unwrap().get(), configure.new_size.1.unwrap().get());
let mut c_surface = compositor.create_surface(wrapper.clone(), w, h);
compositor.configure_surface(&mut c_surface, w, h);
state.surface = Some(c_surface);
}
if let Some((w, h, _, is_dirty)) = auto_size_surfaces.get_mut(id) {
*is_dirty = first || *w != configure.new_size.0.map(|w| w.get()).unwrap_or_default() || *h != configure.new_size.1.map(|h| h.get()).unwrap_or_default();
state.set_logical_size(*w as f32, *h as f32);
} else {
state.set_logical_size(configure.new_size.0.unwrap().get() as f32 , configure.new_size.1.unwrap().get() as f32);
}
state.set_logical_size(w as f32, h as f32);

if first {
let user_interface = build_user_interface(
&application,
Expand Down Expand Up @@ -929,7 +931,7 @@ where

// just draw here immediately and never again for dnd icons
// TODO handle scale factor?
let _new_mouse_interaction = user_interface.draw(
let new_mouse_interaction = user_interface.draw(
&mut renderer,
state.theme(),
&Style {
Expand All @@ -940,6 +942,13 @@ where
state.cursor(),
);

mouse_interaction = new_mouse_interaction;
ev_proxy.send_event(Event::SetCursor(mouse_interaction));
// Pre-emptively remove cursor focus from other surface so they won't set cursor
for state in states.values_mut() {
state.cursor_position = None;
}

let subsurfaces = crate::subsurface_widget::take_subsurfaces();
if let Some(subsurface_state) = subsurface_state.as_mut() {
subsurface_state.update_subsurfaces(
Expand Down Expand Up @@ -1439,7 +1448,11 @@ where
}

debug.draw_finished();
if new_mouse_interaction != mouse_interaction {

// Set cursor if mouse interaction has changed, and surface has pointer focus
if state.cursor_position.is_some()
&& new_mouse_interaction != mouse_interaction
{
mouse_interaction = new_mouse_interaction;
ev_proxy
.send_event(Event::SetCursor(mouse_interaction));
Expand Down
81 changes: 60 additions & 21 deletions sctk/src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::application::SurfaceIdWrapper;
use crate::{
application::Event,
conversion,
dpi::LogicalSize,
dpi::{LogicalPosition, LogicalSize, PhysicalPosition},
handlers::{
activation::IcedRequestData,
wp_fractional_scaling::FractionalScalingManager,
Expand Down Expand Up @@ -47,7 +47,11 @@ use sctk::{
registry::RegistryState,
seat::SeatState,
session_lock::SessionLockState,
shell::{wlr_layer::LayerShell, xdg::XdgShell, WaylandSurface},
shell::{
wlr_layer::{LayerShell, LayerSurface},
xdg::XdgShell,
WaylandSurface,
},
shm::Shm,
};
use sctk::{
Expand Down Expand Up @@ -293,6 +297,8 @@ where
{
let mut control_flow = ControlFlow::Poll;

let mut cursor_position = HashMap::<_, LogicalPosition<f64>>::new();

callback(
IcedSctkEvent::NewEvents(StartCause::Init),
&self.state,
Expand Down Expand Up @@ -577,6 +583,18 @@ where
// Handle pending sctk events.
for event in sctk_event_sink_back_buffer.drain(..) {
match event {
SctkEvent::PointerEvent { ref variant, .. } => {
let surface_id = variant.surface.id();

cursor_position.insert(
surface_id,
LogicalPosition::new(
variant.position.0,
variant.position.1,
),
);
}

SctkEvent::PopupEvent {
variant: PopupEventVariant::Done,
toplevel_id,
Expand Down Expand Up @@ -605,13 +623,18 @@ where
&mut callback,
);
}
None => continue,
None => (),
};

continue;
}

SctkEvent::LayerSurfaceEvent {
variant: LayerSurfaceEventVariant::Done,
id,
} => {
cursor_position.remove(&id.id());

if let Some(i) =
self.state.layer_surfaces.iter().position(|l| {
l.surface.wl_surface().id() == id.id()
Expand All @@ -630,7 +653,10 @@ where
&mut callback,
);
}

continue;
}

SctkEvent::WindowEvent {
variant: WindowEventVariant::Close,
id,
Expand All @@ -654,14 +680,18 @@ where
&mut callback,
);
}

continue;
}
_ => sticky_exit_callback(
IcedSctkEvent::SctkEvent(event),
&self.state,
&mut control_flow,
&mut callback,
),
_ => (),
}

sticky_exit_callback(
IcedSctkEvent::SctkEvent(event),
&self.state,
&mut control_flow,
&mut callback,
)
}

// handle events indirectly via callback to the user.
Expand Down Expand Up @@ -808,9 +838,10 @@ where
},
},
Event::SetCursor(iced_icon) => {
if let Some(ptr) = self.state.seats.get(0).and_then(|s| s.ptr.as_ref()) {
if let Some(seat) = self.state.seats.get_mut(0) {
let icon = conversion::cursor_icon(iced_icon);
let _ = ptr.set_cursor(self.wayland_dispatcher.as_source_ref().connection(), icon);
seat.icon = Some(icon);
seat.set_cursor(self.wayland_dispatcher.as_source_ref().connection(), icon);
}

}
Expand Down Expand Up @@ -845,18 +876,12 @@ where
},
platform_specific::wayland::window::Action::Size { id, width, height } => {
if let Some(window) = self.state.windows.iter_mut().find(|w| w.id == id) {
window.set_size(LogicalSize::new(NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()), NonZeroU32::new(1).unwrap()));
window.set_size(LogicalSize::new(NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()), NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap())));
// TODO Ashley maybe don't force window size?
pending_redraws.push(window.window.wl_surface().id());

if let Some(mut prev_configure) = window.last_configure.clone() {
let (width, height) = (
NonZeroU32::new(width).unwrap_or(NonZeroU32::new(1).unwrap()),
NonZeroU32::new(height).unwrap_or(NonZeroU32::new(1).unwrap()),
);
prev_configure.new_size = (Some(width), Some(height));
if window.last_configure.is_some() {
sticky_exit_callback(
IcedSctkEvent::SctkEvent(SctkEvent::WindowEvent { variant: WindowEventVariant::Configure(prev_configure, window.window.wl_surface().clone(), false), id: window.window.wl_surface().clone()}),
IcedSctkEvent::SctkEvent(SctkEvent::WindowEvent { variant: WindowEventVariant::Size(window.current_size, window.window.wl_surface().clone(), false), id: window.window.wl_surface().clone()}),
&self.state,
&mut control_flow,
&mut callback,
Expand Down Expand Up @@ -937,7 +962,21 @@ where
}
}
},
platform_specific::wayland::window::Action::ShowWindowMenu { id: _, x: _, y: _ } => todo!(),
platform_specific::wayland::window::Action::ShowWindowMenu { id } => {
if let (Some(window), Some((seat, last_press))) = (self.state.windows.iter_mut().find(|w| w.id == id), self.state.seats.first().and_then(|seat| seat.last_ptr_press.map(|p| (&seat.seat, p.2)))) {
let surface_id = window.window.wl_surface().id();

let cursor_position = cursor_position.get(&surface_id)
.cloned()
.unwrap_or_default();

// Cursor position does not need to be scaled here.
let PhysicalPosition { x, y } = cursor_position.to_physical::<i32>(1.0);

window.window.xdg_toplevel().show_window_menu(seat, last_press, x as i32, y as i32);
to_commit.insert(id, window.window.wl_surface().clone());
}
},
platform_specific::wayland::window::Action::Destroy(id) => {
if let Some(i) = self.state.windows.iter().position(|l| l.id == id) {
let window = self.state.windows.remove(i);
Expand Down
Loading

0 comments on commit f9ce292

Please sign in to comment.