mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-28 22:23:47 +01:00
Move XdgShellHandler, begin work on fullscreen/maximized windows
This commit is contained in:
parent
159bebc526
commit
a2887567a3
3 changed files with 335 additions and 310 deletions
320
src/handlers.rs
320
src/handlers.rs
|
@ -1,36 +1,26 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
pub mod xwayland;
|
mod xdg_shell;
|
||||||
|
mod xwayland;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::utils,
|
backend::renderer::utils,
|
||||||
delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_layer_shell,
|
delegate_compositor, delegate_data_device, delegate_fractional_scale, delegate_layer_shell,
|
||||||
delegate_output, delegate_presentation, delegate_primary_selection, delegate_relative_pointer,
|
delegate_output, delegate_presentation, delegate_primary_selection, delegate_relative_pointer,
|
||||||
delegate_seat, delegate_shm, delegate_viewporter, delegate_xdg_shell,
|
delegate_seat, delegate_shm, delegate_viewporter,
|
||||||
desktop::{
|
desktop::{self, layer_map_for_output, PopupKind, WindowSurfaceType},
|
||||||
self, find_popup_root_surface, layer_map_for_output, utils::surface_primary_scanout_output,
|
input::{pointer::CursorImageStatus, Seat, SeatHandler, SeatState},
|
||||||
PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window,
|
|
||||||
WindowSurfaceType,
|
|
||||||
},
|
|
||||||
input::{
|
|
||||||
pointer::{CursorImageStatus, Focus},
|
|
||||||
Seat, SeatHandler, SeatState,
|
|
||||||
},
|
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::Interest,
|
calloop::Interest,
|
||||||
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_buffer::WlBuffer, wl_data_source::WlDataSource, wl_output::WlOutput,
|
wl_buffer::WlBuffer, wl_data_source::WlDataSource, wl_output::WlOutput,
|
||||||
wl_seat::WlSeat, wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
Client, Resource,
|
Client, Resource,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Serial, SERIAL_COUNTER},
|
|
||||||
wayland::{
|
wayland::{
|
||||||
buffer::BufferHandler,
|
buffer::BufferHandler,
|
||||||
compositor::{
|
compositor::{
|
||||||
|
@ -49,10 +39,7 @@ use smithay::{
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
shell::{
|
shell::{
|
||||||
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
|
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
|
||||||
xdg::{
|
xdg::{XdgPopupSurfaceData, XdgToplevelSurfaceData},
|
||||||
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData,
|
|
||||||
XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
},
|
},
|
||||||
|
@ -63,7 +50,7 @@ use crate::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
focus::FocusTarget,
|
focus::FocusTarget,
|
||||||
state::{CalloopData, ClientState, State, WithState},
|
state::{CalloopData, ClientState, State, WithState},
|
||||||
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
|
window::{window_state::WindowResizeState, WindowElement},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<B: Backend> BufferHandler for State<B> {
|
impl<B: Backend> BufferHandler for State<B> {
|
||||||
|
@ -136,21 +123,12 @@ impl<B: Backend> CompositorHandler for State<B> {
|
||||||
if let WindowResizeState::Acknowledged(new_pos) = state.resize_state {
|
if let WindowResizeState::Acknowledged(new_pos) = state.resize_state {
|
||||||
state.resize_state = WindowResizeState::Idle;
|
state.resize_state = WindowResizeState::Idle;
|
||||||
if window.is_x11() {
|
if window.is_x11() {
|
||||||
tracing::error!("DID SOMETHING WITH x11 WINDOW HERE");
|
tracing::warn!("did something with X11 window here");
|
||||||
// if !surface.is_override_redirect() {
|
|
||||||
// surface.set_mapped(true).expect("failed to map x11 win");
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
self.space.map_element(window.clone(), new_pos, false);
|
self.space.map_element(window.clone(), new_pos, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// let states = self
|
|
||||||
// .windows
|
|
||||||
// .iter()
|
|
||||||
// .map(|win| win.with_state(|state| state.resize_state.clone()))
|
|
||||||
// .collect::<Vec<_>>();
|
|
||||||
// tracing::debug!("states: {states:?}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||||
|
@ -202,6 +180,7 @@ fn ensure_initial_configure<B: Backend>(surface: &WlSurface, state: &mut State<B
|
||||||
.send_configure()
|
.send_configure()
|
||||||
.expect("popup initial configure failed");
|
.expect("popup initial configure failed");
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(output) = state.space.outputs().find(|op| {
|
if let Some(output) = state.space.outputs().find(|op| {
|
||||||
|
@ -374,285 +353,6 @@ impl<B: Backend> ShmHandler for State<B> {
|
||||||
}
|
}
|
||||||
delegate_shm!(@<B: Backend> State<B>);
|
delegate_shm!(@<B: Backend> State<B>);
|
||||||
|
|
||||||
impl<B: Backend> XdgShellHandler for State<B> {
|
|
||||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
|
||||||
&mut self.xdg_shell_state
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
|
||||||
let window = WindowElement::Wayland(Window::new(surface));
|
|
||||||
|
|
||||||
{
|
|
||||||
let WindowElement::Wayland(window) = &window else { unreachable!() };
|
|
||||||
window.toplevel().with_pending_state(|tl_state| {
|
|
||||||
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
|
||||||
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
|
||||||
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
|
||||||
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.with_state(|state| {
|
|
||||||
state.tags = match (
|
|
||||||
&self.focus_state.focused_output,
|
|
||||||
self.space.outputs().next(),
|
|
||||||
) {
|
|
||||||
(Some(output), _) | (None, Some(output)) => output.with_state(|state| {
|
|
||||||
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
|
|
||||||
if !output_tags.is_empty() {
|
|
||||||
output_tags
|
|
||||||
} else if let Some(first_tag) = state.tags.first() {
|
|
||||||
vec![first_tag.clone()]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
(None, None) => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing::debug!("new window, tags are {:?}", state.tags);
|
|
||||||
});
|
|
||||||
|
|
||||||
let windows_on_output = self
|
|
||||||
.windows
|
|
||||||
.iter()
|
|
||||||
.filter(|win| {
|
|
||||||
win.with_state(|state| {
|
|
||||||
self.focus_state
|
|
||||||
.focused_output
|
|
||||||
.as_ref()
|
|
||||||
.expect("no focused output")
|
|
||||||
.with_state(|op_state| {
|
|
||||||
op_state
|
|
||||||
.tags
|
|
||||||
.iter()
|
|
||||||
.any(|tag| state.tags.iter().any(|tg| tg == tag))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
self.windows.push(window.clone());
|
|
||||||
// self.space.map_element(window.clone(), (0, 0), true);
|
|
||||||
if let Some(focused_output) = self.focus_state.focused_output.clone() {
|
|
||||||
focused_output.with_state(|state| {
|
|
||||||
let first_tag = state.focused_tags().next();
|
|
||||||
if let Some(first_tag) = first_tag {
|
|
||||||
first_tag.layout().layout(
|
|
||||||
self.windows.clone(),
|
|
||||||
state.focused_tags().cloned().collect(),
|
|
||||||
&mut self.space,
|
|
||||||
&focused_output,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
tracing::debug!(
|
|
||||||
"blocker {}",
|
|
||||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
|
||||||
);
|
|
||||||
for win in windows_on_output.iter() {
|
|
||||||
if let Some(surf) = win.wl_surface() {
|
|
||||||
compositor::add_blocker(&surf, WindowBlocker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let clone = window.clone();
|
|
||||||
self.loop_handle.insert_idle(|data| {
|
|
||||||
crate::state::schedule_on_commit(data, vec![clone], move |data| {
|
|
||||||
BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
tracing::debug!(
|
|
||||||
"blocker {}",
|
|
||||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
|
||||||
);
|
|
||||||
for client in windows_on_output
|
|
||||||
.iter()
|
|
||||||
.filter_map(|win| win.wl_surface()?.client())
|
|
||||||
{
|
|
||||||
data.state
|
|
||||||
.client_compositor_state(&client)
|
|
||||||
.blocker_cleared(&mut data.state, &data.display.handle())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.loop_handle.insert_idle(move |data| {
|
|
||||||
data.state
|
|
||||||
.seat
|
|
||||||
.get_keyboard()
|
|
||||||
.expect("Seat had no keyboard") // FIXME: actually handle error
|
|
||||||
.set_focus(
|
|
||||||
&mut data.state,
|
|
||||||
Some(FocusTarget::Window(window)),
|
|
||||||
SERIAL_COUNTER.next_serial(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
|
||||||
tracing::debug!("toplevel destroyed");
|
|
||||||
self.windows.retain(|window| {
|
|
||||||
window
|
|
||||||
.wl_surface()
|
|
||||||
.is_some_and(|surf| &surf != surface.wl_surface())
|
|
||||||
});
|
|
||||||
if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() {
|
|
||||||
focused_output.with_state(|state| {
|
|
||||||
let first_tag = state.focused_tags().next();
|
|
||||||
if let Some(first_tag) = first_tag {
|
|
||||||
first_tag.layout().layout(
|
|
||||||
self.windows.clone(),
|
|
||||||
state.focused_tags().cloned().collect(),
|
|
||||||
&mut self.space,
|
|
||||||
&focused_output,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// let mut windows: Vec<Window> = self.space.elements().cloned().collect();
|
|
||||||
// windows.retain(|window| window.toplevel() != &surface);
|
|
||||||
// Layouts::master_stack(self, windows, crate::layout::Direction::Left);
|
|
||||||
let focus = self.focus_state.current_focus().map(FocusTarget::Window);
|
|
||||||
self.seat
|
|
||||||
.get_keyboard()
|
|
||||||
.expect("Seat had no keyboard")
|
|
||||||
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
|
||||||
if let Err(err) = self.popup_manager.track_popup(PopupKind::from(surface)) {
|
|
||||||
tracing::warn!("failed to track popup: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) {
|
|
||||||
tracing::debug!("move_request_client");
|
|
||||||
const BUTTON_LEFT: u32 = 0x110; // We assume the left mouse button is used
|
|
||||||
crate::grab::move_grab::move_request_client(
|
|
||||||
self,
|
|
||||||
surface.wl_surface(),
|
|
||||||
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
|
||||||
serial,
|
|
||||||
BUTTON_LEFT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize_request(
|
|
||||||
&mut self,
|
|
||||||
surface: ToplevelSurface,
|
|
||||||
seat: WlSeat,
|
|
||||||
serial: Serial,
|
|
||||||
edges: ResizeEdge,
|
|
||||||
) {
|
|
||||||
const BUTTON_LEFT: u32 = 0x110;
|
|
||||||
crate::grab::resize_grab::resize_request_client(
|
|
||||||
self,
|
|
||||||
surface.wl_surface(),
|
|
||||||
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
|
||||||
serial,
|
|
||||||
edges.into(),
|
|
||||||
BUTTON_LEFT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reposition_request(
|
|
||||||
&mut self,
|
|
||||||
surface: PopupSurface,
|
|
||||||
positioner: PositionerState,
|
|
||||||
token: u32,
|
|
||||||
) {
|
|
||||||
surface.with_pending_state(|state| {
|
|
||||||
state.geometry = positioner.get_geometry();
|
|
||||||
state.positioner = positioner;
|
|
||||||
});
|
|
||||||
surface.send_repositioned(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
|
||||||
let seat: Seat<Self> = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat");
|
|
||||||
let popup_kind = PopupKind::Xdg(surface);
|
|
||||||
if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| {
|
|
||||||
self.window_for_surface(&root)
|
|
||||||
.map(FocusTarget::Window)
|
|
||||||
.or_else(|| {
|
|
||||||
self.space.outputs().find_map(|op| {
|
|
||||||
layer_map_for_output(op)
|
|
||||||
.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)
|
|
||||||
.cloned()
|
|
||||||
.map(FocusTarget::LayerSurface)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}) {
|
|
||||||
if let Ok(mut grab) = self
|
|
||||||
.popup_manager
|
|
||||||
.grab_popup(root, popup_kind, &seat, serial)
|
|
||||||
{
|
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
|
||||||
if keyboard.is_grabbed()
|
|
||||||
&& !(keyboard.has_grab(serial)
|
|
||||||
|| keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))
|
|
||||||
{
|
|
||||||
grab.ungrab(PopupUngrabStrategy::All);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyboard.set_focus(self, grab.current_grab(), serial);
|
|
||||||
keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial);
|
|
||||||
}
|
|
||||||
if let Some(pointer) = seat.get_pointer() {
|
|
||||||
if pointer.is_grabbed()
|
|
||||||
&& !(pointer.has_grab(serial)
|
|
||||||
|| pointer
|
|
||||||
.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))
|
|
||||||
{
|
|
||||||
grab.ungrab(PopupUngrabStrategy::All);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
|
|
||||||
if let Some(window) = self.window_for_surface(&surface) {
|
|
||||||
window.with_state(|state| {
|
|
||||||
if let WindowResizeState::Requested(serial, new_loc) = state.resize_state {
|
|
||||||
match &configure {
|
|
||||||
Configure::Toplevel(configure) => {
|
|
||||||
if configure.serial >= serial {
|
|
||||||
// tracing::debug!("acked configure, new loc is {:?}", new_loc);
|
|
||||||
state.resize_state = WindowResizeState::Acknowledged(new_loc);
|
|
||||||
if let Some(focused_output) =
|
|
||||||
self.focus_state.focused_output.clone()
|
|
||||||
{
|
|
||||||
window.send_frame(
|
|
||||||
&focused_output,
|
|
||||||
self.clock.now(),
|
|
||||||
Some(Duration::ZERO),
|
|
||||||
surface_primary_scanout_output,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Configure::Popup(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn minimize_request(&mut self, surface: ToplevelSurface) {
|
|
||||||
// if let Some(window) = self.window_for_surface(surface.wl_surface()) {
|
|
||||||
// self.space.unmap_elem(&window);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: impl the rest of the fns in XdgShellHandler
|
|
||||||
}
|
|
||||||
delegate_xdg_shell!(@<B: Backend> State<B>);
|
|
||||||
|
|
||||||
delegate_output!(@<B: Backend> State<B>);
|
delegate_output!(@<B: Backend> State<B>);
|
||||||
|
|
||||||
delegate_viewporter!(@<B: Backend> State<B>);
|
delegate_viewporter!(@<B: Backend> State<B>);
|
||||||
|
|
312
src/handlers/xdg_shell.rs
Normal file
312
src/handlers/xdg_shell.rs
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
delegate_xdg_shell,
|
||||||
|
desktop::{
|
||||||
|
find_popup_root_surface, layer_map_for_output, utils::surface_primary_scanout_output,
|
||||||
|
PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window,
|
||||||
|
WindowSurfaceType,
|
||||||
|
},
|
||||||
|
input::{pointer::Focus, Seat},
|
||||||
|
reexports::{
|
||||||
|
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||||
|
wayland_server::{
|
||||||
|
protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
|
||||||
|
Resource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils::{Serial, SERIAL_COUNTER},
|
||||||
|
wayland::{
|
||||||
|
compositor::{self, CompositorHandler},
|
||||||
|
shell::xdg::{
|
||||||
|
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler,
|
||||||
|
XdgShellState,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
backend::Backend,
|
||||||
|
focus::FocusTarget,
|
||||||
|
state::{State, WithState},
|
||||||
|
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<B: Backend> XdgShellHandler for State<B> {
|
||||||
|
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||||
|
&mut self.xdg_shell_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||||
|
let window = WindowElement::Wayland(Window::new(surface));
|
||||||
|
|
||||||
|
{
|
||||||
|
let WindowElement::Wayland(window) = &window else { unreachable!() };
|
||||||
|
window.toplevel().with_pending_state(|tl_state| {
|
||||||
|
tl_state.states.set(xdg_toplevel::State::TiledTop);
|
||||||
|
tl_state.states.set(xdg_toplevel::State::TiledBottom);
|
||||||
|
tl_state.states.set(xdg_toplevel::State::TiledLeft);
|
||||||
|
tl_state.states.set(xdg_toplevel::State::TiledRight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.with_state(|state| {
|
||||||
|
state.tags = match (
|
||||||
|
&self.focus_state.focused_output,
|
||||||
|
self.space.outputs().next(),
|
||||||
|
) {
|
||||||
|
(Some(output), _) | (None, Some(output)) => output.with_state(|state| {
|
||||||
|
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
|
||||||
|
if !output_tags.is_empty() {
|
||||||
|
output_tags
|
||||||
|
} else if let Some(first_tag) = state.tags.first() {
|
||||||
|
vec![first_tag.clone()]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(None, None) => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::debug!("new window, tags are {:?}", state.tags);
|
||||||
|
});
|
||||||
|
|
||||||
|
let windows_on_output = self
|
||||||
|
.windows
|
||||||
|
.iter()
|
||||||
|
.filter(|win| {
|
||||||
|
win.with_state(|state| {
|
||||||
|
self.focus_state
|
||||||
|
.focused_output
|
||||||
|
.as_ref()
|
||||||
|
.expect("no focused output")
|
||||||
|
.with_state(|op_state| {
|
||||||
|
op_state
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.any(|tag| state.tags.iter().any(|tg| tg == tag))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
self.windows.push(window.clone());
|
||||||
|
// self.space.map_element(window.clone(), (0, 0), true);
|
||||||
|
if let Some(focused_output) = self.focus_state.focused_output.clone() {
|
||||||
|
focused_output.with_state(|state| {
|
||||||
|
let first_tag = state.focused_tags().next();
|
||||||
|
if let Some(first_tag) = first_tag {
|
||||||
|
first_tag.layout().layout(
|
||||||
|
self.windows.clone(),
|
||||||
|
state.focused_tags().cloned().collect(),
|
||||||
|
&mut self.space,
|
||||||
|
&focused_output,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
tracing::debug!(
|
||||||
|
"blocker {}",
|
||||||
|
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
|
);
|
||||||
|
for win in windows_on_output.iter() {
|
||||||
|
if let Some(surf) = win.wl_surface() {
|
||||||
|
compositor::add_blocker(&surf, WindowBlocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let clone = window.clone();
|
||||||
|
self.loop_handle.insert_idle(|data| {
|
||||||
|
crate::state::schedule_on_commit(data, vec![clone], move |data| {
|
||||||
|
BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
tracing::debug!(
|
||||||
|
"blocker {}",
|
||||||
|
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
|
);
|
||||||
|
for client in windows_on_output
|
||||||
|
.iter()
|
||||||
|
.filter_map(|win| win.wl_surface()?.client())
|
||||||
|
{
|
||||||
|
data.state
|
||||||
|
.client_compositor_state(&client)
|
||||||
|
.blocker_cleared(&mut data.state, &data.display.handle())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.loop_handle.insert_idle(move |data| {
|
||||||
|
data.state
|
||||||
|
.seat
|
||||||
|
.get_keyboard()
|
||||||
|
.expect("Seat had no keyboard") // FIXME: actually handle error
|
||||||
|
.set_focus(
|
||||||
|
&mut data.state,
|
||||||
|
Some(FocusTarget::Window(window)),
|
||||||
|
SERIAL_COUNTER.next_serial(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
||||||
|
tracing::debug!("toplevel destroyed");
|
||||||
|
self.windows.retain(|window| {
|
||||||
|
window
|
||||||
|
.wl_surface()
|
||||||
|
.is_some_and(|surf| &surf != surface.wl_surface())
|
||||||
|
});
|
||||||
|
if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() {
|
||||||
|
focused_output.with_state(|state| {
|
||||||
|
let first_tag = state.focused_tags().next();
|
||||||
|
if let Some(first_tag) = first_tag {
|
||||||
|
first_tag.layout().layout(
|
||||||
|
self.windows.clone(),
|
||||||
|
state.focused_tags().cloned().collect(),
|
||||||
|
&mut self.space,
|
||||||
|
&focused_output,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// let mut windows: Vec<Window> = self.space.elements().cloned().collect();
|
||||||
|
// windows.retain(|window| window.toplevel() != &surface);
|
||||||
|
// Layouts::master_stack(self, windows, crate::layout::Direction::Left);
|
||||||
|
let focus = self.focus_state.current_focus().map(FocusTarget::Window);
|
||||||
|
self.seat
|
||||||
|
.get_keyboard()
|
||||||
|
.expect("Seat had no keyboard")
|
||||||
|
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||||
|
if let Err(err) = self.popup_manager.track_popup(PopupKind::from(surface)) {
|
||||||
|
tracing::warn!("failed to track popup: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) {
|
||||||
|
tracing::debug!("move_request_client");
|
||||||
|
const BUTTON_LEFT: u32 = 0x110; // We assume the left mouse button is used
|
||||||
|
crate::grab::move_grab::move_request_client(
|
||||||
|
self,
|
||||||
|
surface.wl_surface(),
|
||||||
|
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
||||||
|
serial,
|
||||||
|
BUTTON_LEFT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize_request(
|
||||||
|
&mut self,
|
||||||
|
surface: ToplevelSurface,
|
||||||
|
seat: WlSeat,
|
||||||
|
serial: Serial,
|
||||||
|
edges: ResizeEdge,
|
||||||
|
) {
|
||||||
|
const BUTTON_LEFT: u32 = 0x110;
|
||||||
|
crate::grab::resize_grab::resize_request_client(
|
||||||
|
self,
|
||||||
|
surface.wl_surface(),
|
||||||
|
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
||||||
|
serial,
|
||||||
|
edges.into(),
|
||||||
|
BUTTON_LEFT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reposition_request(
|
||||||
|
&mut self,
|
||||||
|
surface: PopupSurface,
|
||||||
|
positioner: PositionerState,
|
||||||
|
token: u32,
|
||||||
|
) {
|
||||||
|
surface.with_pending_state(|state| {
|
||||||
|
state.geometry = positioner.get_geometry();
|
||||||
|
state.positioner = positioner;
|
||||||
|
});
|
||||||
|
surface.send_repositioned(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
||||||
|
let seat: Seat<Self> = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat");
|
||||||
|
let popup_kind = PopupKind::Xdg(surface);
|
||||||
|
if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| {
|
||||||
|
self.window_for_surface(&root)
|
||||||
|
.map(FocusTarget::Window)
|
||||||
|
.or_else(|| {
|
||||||
|
self.space.outputs().find_map(|op| {
|
||||||
|
layer_map_for_output(op)
|
||||||
|
.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)
|
||||||
|
.cloned()
|
||||||
|
.map(FocusTarget::LayerSurface)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
if let Ok(mut grab) = self
|
||||||
|
.popup_manager
|
||||||
|
.grab_popup(root, popup_kind, &seat, serial)
|
||||||
|
{
|
||||||
|
if let Some(keyboard) = seat.get_keyboard() {
|
||||||
|
if keyboard.is_grabbed()
|
||||||
|
&& !(keyboard.has_grab(serial)
|
||||||
|
|| keyboard.has_grab(grab.previous_serial().unwrap_or(serial)))
|
||||||
|
{
|
||||||
|
grab.ungrab(PopupUngrabStrategy::All);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard.set_focus(self, grab.current_grab(), serial);
|
||||||
|
keyboard.set_grab(PopupKeyboardGrab::new(&grab), serial);
|
||||||
|
}
|
||||||
|
if let Some(pointer) = seat.get_pointer() {
|
||||||
|
if pointer.is_grabbed()
|
||||||
|
&& !(pointer.has_grab(serial)
|
||||||
|
|| pointer
|
||||||
|
.has_grab(grab.previous_serial().unwrap_or_else(|| grab.serial())))
|
||||||
|
{
|
||||||
|
grab.ungrab(PopupUngrabStrategy::All);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
|
||||||
|
if let Some(window) = self.window_for_surface(&surface) {
|
||||||
|
window.with_state(|state| {
|
||||||
|
if let WindowResizeState::Requested(serial, new_loc) = state.resize_state {
|
||||||
|
match &configure {
|
||||||
|
Configure::Toplevel(configure) => {
|
||||||
|
if configure.serial >= serial {
|
||||||
|
// tracing::debug!("acked configure, new loc is {:?}", new_loc);
|
||||||
|
state.resize_state = WindowResizeState::Acknowledged(new_loc);
|
||||||
|
if let Some(focused_output) =
|
||||||
|
self.focus_state.focused_output.clone()
|
||||||
|
{
|
||||||
|
window.send_frame(
|
||||||
|
&focused_output,
|
||||||
|
self.clock.now(),
|
||||||
|
Some(Duration::ZERO),
|
||||||
|
surface_primary_scanout_output,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Configure::Popup(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn minimize_request(&mut self, surface: ToplevelSurface) {
|
||||||
|
// if let Some(window) = self.window_for_surface(surface.wl_surface()) {
|
||||||
|
// self.space.unmap_elem(&window);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: impl the rest of the fns in XdgShellHandler
|
||||||
|
}
|
||||||
|
delegate_xdg_shell!(@<B: Backend> State<B>);
|
13
src/tag.rs
13
src/tag.rs
|
@ -13,6 +13,7 @@ use crate::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
state::{State, WithState},
|
state::{State, WithState},
|
||||||
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
|
static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
@ -44,6 +45,8 @@ struct TagInner {
|
||||||
active: bool,
|
active: bool,
|
||||||
/// What layout this tag has.
|
/// What layout this tag has.
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
|
/// The fullscreen window, if any.
|
||||||
|
fullscreen_window: Option<WindowElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for TagInner {
|
impl PartialEq for TagInner {
|
||||||
|
@ -81,6 +84,14 @@ impl Tag {
|
||||||
pub fn set_layout(&self, layout: Layout) {
|
pub fn set_layout(&self, layout: Layout) {
|
||||||
self.0.borrow_mut().layout = layout;
|
self.0.borrow_mut().layout = layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fullscreen_window(&self) -> Option<WindowElement> {
|
||||||
|
self.0.borrow().fullscreen_window.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fullscreen_window(&self, window: impl Into<Option<WindowElement>>) {
|
||||||
|
self.0.borrow_mut().fullscreen_window = window.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
|
@ -90,8 +101,10 @@ impl Tag {
|
||||||
name,
|
name,
|
||||||
active: false,
|
active: false,
|
||||||
layout: Layout::MasterStack, // TODO: get from config
|
layout: Layout::MasterStack, // TODO: get from config
|
||||||
|
fullscreen_window: None,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output<B: Backend>(&self, state: &State<B>) -> Option<Output> {
|
pub fn output<B: Backend>(&self, state: &State<B>) -> Option<Output> {
|
||||||
state
|
state
|
||||||
.space
|
.space
|
||||||
|
|
Loading…
Reference in a new issue