feat: subsurfaces

This commit is contained in:
Ashley Wulber 2025-03-14 11:16:25 -04:00 • committed by Ashley Wulber
parent 0f37c9922d
commit 93bc4bbd88
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
33 changed files with 1898 additions and 2651 deletions

View file

@ -1,9 +1,9 @@
use iced_futures::futures::channel::oneshot::Sender;
use cctk::sctk::{
activation::{ActivationHandler, RequestData, RequestDataExt},
delegate_activation,
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
};
use iced_futures::futures::channel::oneshot::Sender;
use crate::platform_specific::wayland::event_loop::state::SctkState;

View file

@ -19,7 +19,6 @@ impl KeyboardHandler for SctkState {
_raw: &[u32],
_keysyms: &[Keysym],
) {
self.request_redraw(surface);
let (i, mut is_active, seat) = {
let (i, is_active, my_seat) =
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
@ -32,36 +31,49 @@ impl KeyboardHandler for SctkState {
Some((i, s)) => (i, i == 0, s),
None => return,
};
let surface = if let Some(subsurface) =
self.subsurfaces.iter().find(|s| {
s.steals_keyboard_focus && s.instance.parent == surface.id()
}) {
&subsurface.instance.wl_surface
} else {
surface
};
_ = my_seat.kbd_focus.replace(surface.clone());
let seat = my_seat.seat.clone();
(i, is_active, seat)
};
// TODO Ashley: thoroughly test this
// swap the active seat to be the current seat if the current "active" seat is not focused on the application anyway
if !is_active && self.seats[0].kbd_focus.is_none() {
is_active = true;
self.seats.swap(0, i);
}
self.request_redraw(&surface);
if is_active {
let id = winit::window::WindowId::from_raw(
surface.id().as_ptr() as usize
);
if self.windows.iter().any(|w| w.window.id() == id) {
return;
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == surface.id()).then(|| &s.instance.wl_surface)
});
for surface in surfaces.chain(std::iter::once(surface)) {
if is_active {
let id = winit::window::WindowId::from_raw(
surface.id().as_ptr() as usize,
);
if self.windows.iter().any(|w| w.window.id() == id) {
continue;
}
self.sctk_events.push(SctkEvent::Winit(
id,
winit::event::WindowEvent::Focused(true),
));
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Enter(surface.clone()),
kbd_id: keyboard.clone(),
seat_id: seat.clone(),
surface: surface.clone(),
});
}
self.sctk_events.push(SctkEvent::Winit(
id,
winit::event::WindowEvent::Focused(true),
));
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Enter(surface.clone()),
kbd_id: keyboard.clone(),
seat_id: seat,
surface: surface.clone(),
});
}
}
@ -91,38 +103,42 @@ impl KeyboardHandler for SctkState {
_ = my_seat.kbd_focus.take();
(is_active, seat, kbd)
};
if is_active {
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Leave(surface.clone()),
kbd_id: kbd,
seat_id: seat,
surface: surface.clone(),
});
// if there is another seat with a keyboard focused on a surface make that the new active seat
if let Some(i) =
self.seats.iter().position(|s| s.kbd_focus.is_some())
{
self.seats.swap(0, i);
let s = &self.seats[0];
let id = winit::window::WindowId::from_raw(
surface.id().as_ptr() as usize,
);
if self.windows.iter().any(|w| w.window.id() == id) {
return;
}
self.sctk_events.push(SctkEvent::Winit(
id,
winit::event::WindowEvent::Focused(true),
));
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == surface.id()).then(|| &s.instance.wl_surface)
});
for surface in surfaces.chain(std::iter::once(surface)) {
if is_active {
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Enter(
s.kbd_focus.clone().unwrap(),
),
kbd_id: s.kbd.clone().unwrap(),
seat_id: s.seat.clone(),
variant: KeyboardEventVariant::Leave(surface.clone()),
kbd_id: kbd.clone(),
seat_id: seat.clone(),
surface: surface.clone(),
})
});
// if there is another seat with a keyboard focused on a surface make that the new active seat
if let Some(i) =
self.seats.iter().position(|s| s.kbd_focus.is_some())
{
self.seats.swap(0, i);
let s = &self.seats[0];
let id = winit::window::WindowId::from_raw(
surface.id().as_ptr() as usize,
);
if self.windows.iter().any(|w| w.window.id() == id) {
continue;
}
self.sctk_events.push(SctkEvent::Winit(
id,
winit::event::WindowEvent::Focused(true),
));
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Enter(
s.kbd_focus.clone().unwrap(),
),
kbd_id: s.kbd.clone().unwrap(),
seat_id: s.seat.clone(),
surface: surface.clone(),
})
}
}
}
}
@ -150,35 +166,20 @@ impl KeyboardHandler for SctkState {
let kbd_id = keyboard.clone();
_ = my_seat.last_kbd_press.replace((event.clone(), serial));
if is_active {
// FIXME can't create winit key events because of private field
// if let Some(id) = id {
// let physical_key = raw_keycode_to_physicalkey(event.raw_code);
// let (logical_key, location) =
// keysym_to_vkey_location(event.keysym);
// self.sctk_events.push(SctkEvent::Winit(
// id,
// winit::event::WindowEvent::KeyboardInput {
// device_id: Default::default(),
// event: winit::event::KeyEvent {
// physical_key,
// logical_key,
// text: event.utf8.map(|s| s.into()),
// location,
// state: winit::event::ElementState::Pressed,
// repeat: false, // TODO we don't have this info...
// },
// is_synthetic: false,
// },
// ))
// }
if let Some(surface) = my_seat.kbd_focus.clone() {
self.request_redraw(&surface);
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Press(event),
kbd_id,
seat_id,
surface,
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == surface.id())
.then(|| &s.instance.wl_surface)
});
for surface in surfaces.chain(std::iter::once(&surface)) {
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Press(event.clone()),
kbd_id: kbd_id.clone(),
seat_id: seat_id.clone(),
surface: surface.clone(),
});
}
}
}
}
@ -208,12 +209,18 @@ impl KeyboardHandler for SctkState {
if is_active {
if let Some(surface) = my_seat.kbd_focus.clone() {
self.request_redraw(&surface);
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Release(event),
kbd_id,
seat_id,
surface,
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == surface.id())
.then(|| &s.instance.wl_surface)
});
for surface in surfaces.chain(std::iter::once(&surface)) {
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Release(event.clone()),
kbd_id: kbd_id.clone(),
seat_id: seat_id.clone(),
surface: surface.clone(),
});
}
}
}
}
@ -244,12 +251,20 @@ impl KeyboardHandler for SctkState {
if is_active {
if let Some(surface) = my_seat.kbd_focus.clone() {
self.request_redraw(&surface);
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Modifiers(modifiers),
kbd_id,
seat_id,
surface,
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == surface.id())
.then(|| &s.instance.wl_surface)
});
for surface in surfaces.chain(std::iter::once(&surface)) {
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Modifiers(
modifiers.clone(),
),
kbd_id: kbd_id.clone(),
seat_id: seat_id.clone(),
surface: surface.clone(),
});
}
}
}
}

View file

@ -11,6 +11,7 @@ use cctk::sctk::{
CursorIcon, PointerEvent, PointerEventKind, PointerHandler,
},
};
use iced_futures::core::Point;
use winit::{
dpi::PhysicalPosition,
event::{
@ -56,6 +57,7 @@ impl PointerHandler for SctkState {
if self.windows.iter().any(|w| w.window.id() == id) {
continue;
}
let entry = self
.frame_status
.entry(e.surface.id())
@ -63,6 +65,7 @@ impl PointerHandler for SctkState {
if matches!(entry, FrameStatus::Received) {
*entry = FrameStatus::Ready;
}
if let PointerEventKind::Motion { time } = &e.kind {
self.sctk_events.push(SctkEvent::PointerEvent {
variant: PointerEvent {

View file

@ -6,7 +6,6 @@ use crate::{
event_loop::state::SctkState, sctk_event::SctkEvent,
},
};
use iced_runtime::core::{touch, Point};
use cctk::sctk::{
delegate_touch,
reexports::client::{
@ -15,6 +14,7 @@ use cctk::sctk::{
},
seat::touch::TouchHandler,
};
use iced_runtime::core::{touch, Point};
impl TouchHandler for SctkState {
fn down(

View file

@ -1,5 +1,6 @@
use crate::platform_specific::wayland::{
handlers::SctkState, sctk_event::SctkEvent,
use crate::{
event_loop::state::CommonSurface,
platform_specific::wayland::{handlers::SctkState, sctk_event::SctkEvent},
};
use cctk::sctk::{
delegate_session_lock,
@ -15,8 +16,9 @@ impl SessionLockHandler for SctkState {
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_session_lock: SessionLock,
session_lock: SessionLock,
) {
self.session_lock = Some(session_lock);
self.sctk_events.push(SctkEvent::SessionLocked);
}
@ -44,8 +46,21 @@ impl SessionLockHandler for SctkState {
Some(l) => l,
None => return,
};
lock_surface
.update_viewport(configure.new_size.0, configure.new_size.1);
let first = lock_surface.last_configure.is_none();
_ = lock_surface.last_configure.replace(configure.clone());
self.sctk_events.push(SctkEvent::SessionLockSurfaceCreated {
queue_handle: self.queue_handle.clone(),
surface: CommonSurface::Lock(
lock_surface.session_lock_surface.clone(),
),
native_id: lock_surface.id,
common: lock_surface.common.clone(),
display: self.connection.display(),
});
self.sctk_events
.push(SctkEvent::SessionLockSurfaceConfigure {
surface: session_lock_surface.wl_surface().clone(),

View file

@ -6,6 +6,7 @@ use cctk::sctk::{
delegate_xdg_popup, reexports::client::Proxy,
shell::xdg::popup::PopupHandler,
};
use winit::dpi::LogicalSize;
impl PopupHandler for SctkState {
fn configure(
@ -24,6 +25,9 @@ impl PopupHandler for SctkState {
};
let first = sctk_popup.last_configure.is_none();
_ = sctk_popup.last_configure.replace(configure.clone());
let mut guard = sctk_popup.common.lock().unwrap();
guard.size =
LogicalSize::new(configure.width as u32, configure.height as u32);
self.sctk_events.push(SctkEvent::PopupEvent {
variant: PopupEventVariant::Configure(

View file

@ -1,7 +1,5 @@
use cctk::{
cosmic_protocols::{
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
},
cosmic_protocols::toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
toplevel_management::ToplevelManagerHandler,
wayland_client::{self, WEnum},