wayland: Rework handlers and protocols into separate files
This commit is contained in:
parent
d182d5b388
commit
06d5989223
25 changed files with 3303 additions and 909 deletions
11
src/wayland/handlers/buffer.rs
Normal file
11
src/wayland/handlers/buffer.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
||||
wayland::buffer::BufferHandler,
|
||||
};
|
||||
use crate::utils::prelude::*;
|
||||
|
||||
impl BufferHandler for State {
|
||||
fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
|
||||
}
|
||||
185
src/wayland/handlers/compositor.rs
Normal file
185
src/wayland/handlers/compositor.rs
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
sync::Mutex,
|
||||
};
|
||||
use smithay::{
|
||||
backend::renderer::utils::{
|
||||
on_commit_buffer_handler,
|
||||
},
|
||||
desktop::{
|
||||
LayerSurface,
|
||||
PopupKind,
|
||||
Kind,
|
||||
WindowSurfaceType,
|
||||
},
|
||||
reexports::wayland_server::{
|
||||
DisplayHandle,
|
||||
protocol::wl_surface::WlSurface,
|
||||
},
|
||||
wayland::{
|
||||
compositor::{
|
||||
CompositorHandler,
|
||||
CompositorState,
|
||||
with_states,
|
||||
},
|
||||
shell::{
|
||||
xdg::{
|
||||
ToplevelSurface,
|
||||
XdgToplevelSurfaceRoleAttributes,
|
||||
XdgPopupSurfaceRoleAttributes,
|
||||
},
|
||||
wlr_layer::LayerSurfaceAttributes,
|
||||
},
|
||||
},
|
||||
delegate_compositor,
|
||||
};
|
||||
use crate::{
|
||||
state::BackendData,
|
||||
utils::prelude::*,
|
||||
};
|
||||
|
||||
impl State {
|
||||
fn early_import_surface(&mut self, dh: &DisplayHandle, surface: &WlSurface) {
|
||||
let mut import_nodes = std::collections::HashSet::new();
|
||||
for output in self.common.shell.outputs_for_surface(&surface) {
|
||||
if let BackendData::Kms(ref mut kms_state) = &mut self.backend {
|
||||
if let Some(target) = kms_state.target_node_for_output(&output) {
|
||||
if import_nodes.insert(target) {
|
||||
kms_state.try_early_import(dh, surface, &output, target, &self.common.shell);
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
.backend
|
||||
.schedule_render(&self.common.event_loop_handle, &output);
|
||||
}
|
||||
}
|
||||
|
||||
fn toplevel_ensure_initial_configure(&mut self, toplevel: &ToplevelSurface) -> bool {
|
||||
// send the initial configure if relevant
|
||||
let initial_configure_sent = with_states(toplevel.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgToplevelSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initial_configure_sent
|
||||
});
|
||||
if !initial_configure_sent {
|
||||
// TODO: query expected size from shell (without inserting and mapping)
|
||||
toplevel.with_pending_state(|states| states.size = None);
|
||||
toplevel.send_configure();
|
||||
}
|
||||
initial_configure_sent
|
||||
}
|
||||
|
||||
fn xdg_popup_ensure_initial_configure(&mut self, popup: &PopupKind) {
|
||||
let PopupKind::Xdg(ref popup) = popup;
|
||||
let initial_configure_sent = with_states(popup.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<XdgPopupSurfaceRoleAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initial_configure_sent
|
||||
});
|
||||
if !initial_configure_sent {
|
||||
// NOTE: This should never fail as the initial configure is always
|
||||
// allowed.
|
||||
popup.send_configure().expect("initial configure failed");
|
||||
}
|
||||
}
|
||||
|
||||
fn layer_surface_ensure_inital_configure(&mut self, surface: &LayerSurface) -> bool {
|
||||
// send the initial configure if relevant
|
||||
let initial_configure_sent = with_states(surface.wl_surface(), |states| {
|
||||
states
|
||||
.data_map
|
||||
.get::<Mutex<LayerSurfaceAttributes>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.initial_configure_sent
|
||||
});
|
||||
if !initial_configure_sent {
|
||||
// TODO: we should be able to compute the initial dimensions
|
||||
surface.layer_surface().send_configure();
|
||||
}
|
||||
initial_configure_sent
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorHandler for State {
|
||||
fn compositor_state(&mut self) -> &mut CompositorState {
|
||||
&mut self.common.compositor_state
|
||||
}
|
||||
|
||||
fn commit(&mut self, dh: &DisplayHandle, surface: &WlSurface) {
|
||||
// initial configure
|
||||
if let Some((window, seat)) = self.common.shell.pending_windows.iter().find(|(window, _)| {
|
||||
window.toplevel().wl_surface() == surface
|
||||
}).cloned() {
|
||||
match window.toplevel() {
|
||||
Kind::Xdg(toplevel) => {
|
||||
if self.toplevel_ensure_initial_configure(&toplevel) {
|
||||
let output = active_output(&seat, &self.common);
|
||||
self.common.shell.map_window(&window, &output);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((layer_surface, _, _)) = self.common.shell.pending_layers.iter().find(|(layer_surface, _, _)| {
|
||||
layer_surface.wl_surface() == surface
|
||||
}).cloned() {
|
||||
if self.layer_surface_ensure_inital_configure(&layer_surface) {
|
||||
self.common.shell.map_layer(&layer_surface, dh);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(popup) = self.common.shell.popups.find_popup(surface) {
|
||||
self.xdg_popup_ensure_initial_configure(&popup);
|
||||
}
|
||||
|
||||
// If we would re-position the window inside the grab we would get a weird jittery animation.
|
||||
// We only want to resize once the client has acknoledged & commited the new size,
|
||||
// so we need to carefully track the state through different handlers.
|
||||
if let Some((space, window)) =
|
||||
self.common
|
||||
.shell
|
||||
.space_for_surface_mut(surface)
|
||||
.and_then(|workspace| {
|
||||
workspace
|
||||
.space
|
||||
.window_for_surface(surface, WindowSurfaceType::TOPLEVEL)
|
||||
.cloned()
|
||||
.map(|window| (&mut workspace.space, window))
|
||||
})
|
||||
{
|
||||
let new_location = crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_state(
|
||||
&window,
|
||||
space.window_location(&window).unwrap(),
|
||||
window.geometry().size,
|
||||
);
|
||||
if let Some(location) = new_location {
|
||||
space.map_window(&window, location, true);
|
||||
}
|
||||
}
|
||||
|
||||
on_commit_buffer_handler(surface);
|
||||
self.early_import_surface(dh, surface);
|
||||
self.common.shell.popups.commit(surface);
|
||||
for workspace in &self.common.shell.spaces {
|
||||
workspace.space.commit(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(State);
|
||||
62
src/wayland/handlers/data_device.rs
Normal file
62
src/wayland/handlers/data_device.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::cell::RefCell;
|
||||
use smithay::{
|
||||
reexports::wayland_server::protocol::{
|
||||
wl_data_source::WlDataSource,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
wayland::{
|
||||
data_device::{
|
||||
DataDeviceState,
|
||||
DataDeviceHandler,
|
||||
ClientDndGrabHandler,
|
||||
ServerDndGrabHandler,
|
||||
},
|
||||
seat::Seat,
|
||||
},
|
||||
delegate_data_device,
|
||||
};
|
||||
use crate::state::State;
|
||||
|
||||
pub struct DnDIcon {
|
||||
surface: RefCell<Option<WlSurface>>,
|
||||
}
|
||||
|
||||
pub fn get_dnd_icon(seat: &Seat<State>) -> Option<WlSurface> {
|
||||
let userdata = seat.user_data();
|
||||
userdata
|
||||
.get::<DnDIcon>()
|
||||
.and_then(|x| x.surface.borrow().clone())
|
||||
}
|
||||
|
||||
impl ClientDndGrabHandler for State {
|
||||
fn started(
|
||||
&mut self,
|
||||
_source: Option<WlDataSource>,
|
||||
icon: Option<WlSurface>,
|
||||
seat: Seat<Self>
|
||||
) {
|
||||
let user_data = seat.user_data();
|
||||
user_data.insert_if_missing(|| DnDIcon {
|
||||
surface: RefCell::new(None),
|
||||
});
|
||||
*user_data.get::<DnDIcon>().unwrap().surface.borrow_mut() = icon;
|
||||
}
|
||||
fn dropped(&mut self, seat: Seat<Self>) {
|
||||
seat.user_data()
|
||||
.get::<DnDIcon>()
|
||||
.unwrap()
|
||||
.surface
|
||||
.borrow_mut()
|
||||
.take();
|
||||
}
|
||||
}
|
||||
impl ServerDndGrabHandler for State {}
|
||||
impl DataDeviceHandler for State {
|
||||
fn data_device_state(&self) -> &DataDeviceState {
|
||||
&self.common.data_device_state
|
||||
}
|
||||
}
|
||||
|
||||
delegate_data_device!(State);
|
||||
38
src/wayland/handlers/dmabuf.rs
Normal file
38
src/wayland/handlers/dmabuf.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
backend::{
|
||||
allocator::dmabuf::Dmabuf,
|
||||
renderer::ImportDma,
|
||||
},
|
||||
reexports::wayland_server::DisplayHandle,
|
||||
wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportError},
|
||||
delegate_dmabuf,
|
||||
};
|
||||
use crate::state::{BackendData, State};
|
||||
|
||||
impl DmabufHandler for State {
|
||||
fn dmabuf_state(&mut self) -> &mut DmabufState {
|
||||
&mut self.common.dmabuf_state
|
||||
}
|
||||
|
||||
fn dmabuf_imported(&mut self, dh: &DisplayHandle, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<(), ImportError> {
|
||||
match &mut self.backend {
|
||||
BackendData::Kms(ref mut state) => state
|
||||
.dmabuf_imported(dh, global, dmabuf)
|
||||
.map_err(|_| ImportError::Failed),
|
||||
BackendData::Winit(ref mut state) => state.backend
|
||||
.renderer()
|
||||
.import_dmabuf(&dmabuf, None)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ImportError::Failed),
|
||||
BackendData::X11(ref mut state) => state.renderer
|
||||
.import_dmabuf(&dmabuf, None)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ImportError::Failed),
|
||||
_ => unreachable!("No backend set when importing dmabuf"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dmabuf!(State);
|
||||
49
src/wayland/handlers/layer_shell.rs
Normal file
49
src/wayland/handlers/layer_shell.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
desktop::LayerSurface,
|
||||
reexports::wayland_server::{
|
||||
DisplayHandle,
|
||||
protocol::wl_output::WlOutput,
|
||||
},
|
||||
wayland::{
|
||||
output::Output,
|
||||
shell::wlr_layer::{
|
||||
WlrLayerShellHandler,
|
||||
WlrLayerShellState,
|
||||
LayerSurface as WlrLayerSurface,
|
||||
Layer,
|
||||
},
|
||||
},
|
||||
delegate_layer_shell,
|
||||
};
|
||||
use crate::utils::prelude::*;
|
||||
|
||||
impl WlrLayerShellHandler for State {
|
||||
fn shell_state(&mut self) -> &mut WlrLayerShellState {
|
||||
&mut self.common.shell.layer_shell_state
|
||||
}
|
||||
|
||||
fn new_layer_surface(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: WlrLayerSurface,
|
||||
wl_output: Option<WlOutput>,
|
||||
_layer: Layer,
|
||||
namespace: String
|
||||
) {
|
||||
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
|
||||
let seat = self.common.last_active_seat.clone();
|
||||
let output = wl_output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.unwrap_or_else(|| active_output(&seat, &self.common));
|
||||
self.common.shell.pending_layers.push((
|
||||
LayerSurface::new(surface, namespace),
|
||||
output,
|
||||
seat,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
delegate_layer_shell!(State);
|
||||
39
src/wayland/handlers/mod.rs
Normal file
39
src/wayland/handlers/mod.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub mod buffer;
|
||||
pub mod compositor;
|
||||
pub mod data_device;
|
||||
pub mod dmabuf;
|
||||
pub mod layer_shell;
|
||||
pub mod output;
|
||||
pub mod output_configuration;
|
||||
pub mod seat;
|
||||
pub mod shm;
|
||||
pub mod toplevel_info;
|
||||
pub mod viewporter;
|
||||
pub mod wl_drm;
|
||||
pub mod workspace;
|
||||
pub mod xdg_shell;
|
||||
|
||||
use crate::state::Common;
|
||||
use smithay::{
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
wayland::compositor::{add_destruction_hook, with_states},
|
||||
};
|
||||
|
||||
fn mark_dirty_on_drop(state: &Common, wl_surface: &WlSurface) {
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
let dirty = state.dirty_flag.clone();
|
||||
struct DirtyFlag(Arc<AtomicBool>);
|
||||
|
||||
with_states(wl_surface, |data| {
|
||||
data.data_map.insert_if_missing(|| DirtyFlag(dirty));
|
||||
});
|
||||
add_destruction_hook(wl_surface, |data| if let Some(DirtyFlag(dirty)) = data.data_map.get::<DirtyFlag>() {
|
||||
dirty.store(true, Ordering::SeqCst);
|
||||
})
|
||||
}
|
||||
6
src/wayland/handlers/output.rs
Normal file
6
src/wayland/handlers/output.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::delegate_output;
|
||||
use crate::state::State;
|
||||
|
||||
delegate_output!(State);
|
||||
141
src/wayland/handlers/output_configuration.rs
Normal file
141
src/wayland/handlers/output_configuration.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::wayland::output::Output;
|
||||
|
||||
use crate::{
|
||||
config::OutputConfig,
|
||||
state::State,
|
||||
wayland::protocols::output_configuration::{
|
||||
OutputConfigurationState,
|
||||
OutputConfigurationHandler,
|
||||
OutputConfiguration,
|
||||
ModeConfiguration,
|
||||
delegate_output_configuration,
|
||||
},
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl OutputConfigurationHandler for State {
|
||||
fn output_configuration_state(&mut self) -> &mut OutputConfigurationState<Self> {
|
||||
&mut self.common.output_configuration_state
|
||||
}
|
||||
|
||||
fn test_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
|
||||
self.output_configuration(true, conf)
|
||||
}
|
||||
fn apply_configuration(&mut self, conf: Vec<(Output, OutputConfiguration)>) -> bool {
|
||||
self.output_configuration(false, conf)
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn output_configuration(&mut self, test_only: bool, conf: Vec<(Output, OutputConfiguration)>) -> bool {
|
||||
if conf.iter().all(|(_, conf)| matches!(conf, OutputConfiguration::Disabled)) {
|
||||
return false; // we don't allow the user to accidentally disable all their outputs
|
||||
}
|
||||
|
||||
let mut backups = Vec::new();
|
||||
for (output, conf) in &conf {
|
||||
{
|
||||
let mut current_config = output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
backups.push((output, current_config.clone()));
|
||||
|
||||
if let OutputConfiguration::Enabled {
|
||||
mode,
|
||||
scale,
|
||||
transform,
|
||||
position,
|
||||
} = conf {
|
||||
match mode {
|
||||
Some(ModeConfiguration::Mode(mode)) => {
|
||||
current_config.mode =
|
||||
((mode.size.w, mode.size.h), Some(mode.refresh as u32));
|
||||
}
|
||||
Some(ModeConfiguration::Custom { size, refresh }) => {
|
||||
current_config.mode =
|
||||
((size.w, size.h), refresh.map(|x| x as u32));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(scale) = scale {
|
||||
current_config.scale = *scale;
|
||||
}
|
||||
if let Some(transform) = transform {
|
||||
current_config.transform = *transform;
|
||||
}
|
||||
if let Some(position) = position {
|
||||
current_config.position = (*position).into();
|
||||
}
|
||||
current_config.enabled = true;
|
||||
} else {
|
||||
current_config.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = self.backend.apply_config_for_output(
|
||||
output,
|
||||
test_only,
|
||||
&mut self.common.shell,
|
||||
&self.common.event_loop_handle,
|
||||
) {
|
||||
slog_scope::warn!(
|
||||
"Failed to apply config to {}: {}. Resetting",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
for (output, backup) in backups {
|
||||
{
|
||||
let mut current_config = output
|
||||
.user_data()
|
||||
.get::<RefCell<OutputConfig>>()
|
||||
.unwrap()
|
||||
.borrow_mut();
|
||||
*current_config = backup;
|
||||
}
|
||||
if !test_only {
|
||||
if let Err(err) = self.backend.apply_config_for_output(
|
||||
output,
|
||||
false,
|
||||
&mut self.common.shell,
|
||||
&self.common.event_loop_handle,
|
||||
) {
|
||||
slog_scope::error!(
|
||||
"Failed to reset output config for {}: {}",
|
||||
output.name(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for output in conf.iter().filter(|(_, c)| matches!(c, OutputConfiguration::Enabled { .. })).map(|(o, _)| o) {
|
||||
self.common.output_configuration_state.enable_head(output);
|
||||
}
|
||||
for output in conf.iter().filter(|(_, c)| matches!(c, OutputConfiguration::Disabled)).map(|(o, _)| o) {
|
||||
self.common.output_configuration_state.disable_head(output);
|
||||
}
|
||||
self
|
||||
.common
|
||||
.config
|
||||
.write_outputs(self.common.output_configuration_state.outputs());
|
||||
self.common.event_loop_handle.insert_idle(move |data| {
|
||||
data
|
||||
.state
|
||||
.common
|
||||
.output_configuration_state
|
||||
.update();
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
delegate_output_configuration!(State);
|
||||
15
src/wayland/handlers/seat.rs
Normal file
15
src/wayland/handlers/seat.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
wayland::seat::{SeatHandler, SeatState},
|
||||
delegate_seat,
|
||||
};
|
||||
use crate::state::State;
|
||||
|
||||
impl SeatHandler for State {
|
||||
fn seat_state(&mut self) -> &mut SeatState<Self> {
|
||||
&mut self.common.seat_state
|
||||
}
|
||||
}
|
||||
|
||||
delegate_seat!(State);
|
||||
15
src/wayland/handlers/shm.rs
Normal file
15
src/wayland/handlers/shm.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
wayland::shm::{ShmHandler, ShmState},
|
||||
delegate_shm,
|
||||
};
|
||||
use crate::state::State;
|
||||
|
||||
impl ShmHandler for State {
|
||||
fn shm_state(&self) -> &ShmState {
|
||||
&self.common.shm_state
|
||||
}
|
||||
}
|
||||
|
||||
delegate_shm!(State);
|
||||
21
src/wayland/handlers/toplevel_info.rs
Normal file
21
src/wayland/handlers/toplevel_info.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
state::State,
|
||||
wayland::protocols::toplevel_info::{
|
||||
ToplevelInfoHandler,
|
||||
ToplevelInfoState,
|
||||
delegate_toplevel_info,
|
||||
},
|
||||
};
|
||||
|
||||
impl ToplevelInfoHandler for State {
|
||||
fn toplevel_info_state(&self) -> &ToplevelInfoState<State> {
|
||||
&self.common.shell.toplevel_info_state
|
||||
}
|
||||
fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState<State> {
|
||||
&mut self.common.shell.toplevel_info_state
|
||||
}
|
||||
}
|
||||
|
||||
delegate_toplevel_info!(State);
|
||||
6
src/wayland/handlers/viewporter.rs
Normal file
6
src/wayland/handlers/viewporter.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::delegate_viewporter;
|
||||
use crate::state::State;
|
||||
|
||||
delegate_viewporter!(State);
|
||||
8
src/wayland/handlers/wl_drm.rs
Normal file
8
src/wayland/handlers/wl_drm.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
state::State,
|
||||
wayland::protocols::drm::delegate_wl_drm,
|
||||
};
|
||||
|
||||
delegate_wl_drm!(State);
|
||||
48
src/wayland/handlers/workspace.rs
Normal file
48
src/wayland/handlers/workspace.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::reexports::wayland_server::DisplayHandle;
|
||||
use crate::{
|
||||
state::ClientState,
|
||||
wayland::protocols::workspace::{
|
||||
Request,
|
||||
WorkspaceHandler,
|
||||
WorkspaceState,
|
||||
WorkspaceClientHandler,
|
||||
WorkspaceClientState,
|
||||
delegate_workspace,
|
||||
},
|
||||
utils::prelude::*,
|
||||
};
|
||||
|
||||
impl WorkspaceClientHandler for ClientState {
|
||||
fn workspace_state(&self) -> &WorkspaceClientState {
|
||||
&self.workspace_client_state
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkspaceHandler for State {
|
||||
type Client = ClientState;
|
||||
fn workspace_state(&self) -> &WorkspaceState<Self> {
|
||||
&self.common.shell.workspace_state
|
||||
}
|
||||
fn workspace_state_mut(&mut self) -> &mut WorkspaceState<Self> {
|
||||
&mut self.common.shell.workspace_state
|
||||
}
|
||||
|
||||
fn commit_requests(&mut self, dh: &DisplayHandle, requests: Vec<Request>) {
|
||||
for request in requests.into_iter() {
|
||||
match request {
|
||||
Request::Activate(handle) => {
|
||||
if let Some(idx) = self.common.shell.spaces.iter().position(|w| w.handle == handle) {
|
||||
let seat = &self.common.last_active_seat;
|
||||
let output = active_output(seat, &self.common);
|
||||
self.common.shell.activate(dh, seat, &output, idx);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_workspace!(State);
|
||||
298
src/wayland/handlers/xdg_shell.rs
Normal file
298
src/wayland/handlers/xdg_shell.rs
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use smithay::{
|
||||
desktop::{
|
||||
Kind,
|
||||
PopupKind,
|
||||
PopupUngrabStrategy,
|
||||
PopupKeyboardGrab,
|
||||
PopupPointerGrab,
|
||||
PopupGrab,
|
||||
Window,
|
||||
WindowSurfaceType,
|
||||
},
|
||||
reexports::{
|
||||
wayland_server::{
|
||||
DisplayHandle,
|
||||
protocol::{
|
||||
wl_seat::WlSeat,
|
||||
wl_surface::WlSurface,
|
||||
wl_output::WlOutput,
|
||||
},
|
||||
},
|
||||
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
},
|
||||
wayland::{
|
||||
shell::xdg::{
|
||||
XdgShellHandler,
|
||||
XdgShellState,
|
||||
ToplevelSurface,
|
||||
PopupSurface,
|
||||
PositionerState,
|
||||
Configure,
|
||||
},
|
||||
seat::{
|
||||
Seat,
|
||||
PointerGrabStartData,
|
||||
},
|
||||
output::Output,
|
||||
Serial,
|
||||
},
|
||||
delegate_xdg_shell,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use crate::utils::prelude::*;
|
||||
|
||||
pub type PopupGrabData = Cell<Option<PopupGrab>>;
|
||||
|
||||
impl XdgShellHandler for State {
|
||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||
&mut self.common.shell.xdg_shell_state
|
||||
}
|
||||
|
||||
fn new_toplevel(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
|
||||
|
||||
let seat = &self.common.last_active_seat;
|
||||
let window = Window::new(Kind::Xdg(surface));
|
||||
self.common.shell.toplevel_info_state.new_toplevel(&window);
|
||||
self.common.shell.pending_windows.push((window, seat.clone()));
|
||||
// We will position the window after the first commit, when we know its size hints
|
||||
}
|
||||
|
||||
fn new_popup(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: PopupSurface,
|
||||
_positioner: PositionerState,
|
||||
) {
|
||||
super::mark_dirty_on_drop(&self.common, surface.wl_surface());
|
||||
|
||||
self.common
|
||||
.shell
|
||||
.popups
|
||||
.track_popup(PopupKind::from(surface))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn ack_configure(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: WlSurface,
|
||||
configure: Configure
|
||||
) {
|
||||
if let Configure::Toplevel(configure) = configure {
|
||||
// If we would re-position the window inside the grab we would get a weird jittery animation.
|
||||
// We only want to resize once the client has acknoledged & commited the new size,
|
||||
// so we need to carefully track the state through different handlers.
|
||||
if let Some(window) = self.common
|
||||
.shell
|
||||
.space_for_surface(&surface)
|
||||
.and_then(|workspace| workspace.space.window_for_surface(&surface, WindowSurfaceType::TOPLEVEL))
|
||||
{
|
||||
crate::shell::layout::floating::ResizeSurfaceGrab::ack_configure(
|
||||
window, configure,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grab(
|
||||
&mut self,
|
||||
dh: &DisplayHandle,
|
||||
surface: PopupSurface,
|
||||
seat: WlSeat,
|
||||
serial: Serial,
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
let ret = self.common.shell.popups.grab_popup(dh, surface.into(), &seat, serial);
|
||||
|
||||
if let Ok(mut grab) = ret {
|
||||
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(dh, PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
self.common.set_focus(dh, grab.current_grab().as_ref(), &seat, Some(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(dh, PopupUngrabStrategy::All);
|
||||
return;
|
||||
}
|
||||
pointer.set_grab(PopupPointerGrab::new(&grab), serial, 0);
|
||||
}
|
||||
|
||||
seat.user_data()
|
||||
.insert_if_missing(|| PopupGrabData::new(None));
|
||||
seat.user_data()
|
||||
.get::<PopupGrabData>()
|
||||
.unwrap()
|
||||
.set(Some(grab));
|
||||
}
|
||||
}
|
||||
|
||||
fn reposition_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: PopupSurface,
|
||||
positioner: PositionerState,
|
||||
token: u32
|
||||
) {
|
||||
surface.with_pending_state(|state| {
|
||||
// TODO: This is a simplification, a proper compositor would
|
||||
// calculate the geometry of the popup here.
|
||||
// For now we just use the default implementation here that does not take the
|
||||
// window position and output constraints into account.
|
||||
let geometry = positioner.get_geometry();
|
||||
state.geometry = geometry;
|
||||
state.positioner = positioner;
|
||||
});
|
||||
surface.send_repositioned(token);
|
||||
}
|
||||
|
||||
fn move_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: ToplevelSurface,
|
||||
seat: WlSeat,
|
||||
serial: Serial
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
if let Some(start_data) =
|
||||
check_grab_preconditions(&seat, surface.wl_surface(), serial)
|
||||
{
|
||||
let workspace = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_surface_mut(surface.wl_surface())
|
||||
.unwrap();
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
workspace.move_request(&window, &seat, serial, start_data);
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_request(
|
||||
&mut self,
|
||||
_dh: &DisplayHandle,
|
||||
surface: ToplevelSurface,
|
||||
seat: WlSeat,
|
||||
serial: Serial,
|
||||
edges: xdg_toplevel::ResizeEdge
|
||||
) {
|
||||
let seat = Seat::from_resource(&seat).unwrap();
|
||||
if let Some(start_data) =
|
||||
check_grab_preconditions(&seat, surface.wl_surface(), serial)
|
||||
{
|
||||
let workspace = self
|
||||
.common
|
||||
.shell
|
||||
.space_for_surface_mut(surface.wl_surface())
|
||||
.unwrap();
|
||||
let window = workspace
|
||||
.space
|
||||
.window_for_surface(surface.wl_surface(), WindowSurfaceType::TOPLEVEL)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
workspace.resize_request(&window, &seat, serial, start_data, edges);
|
||||
}
|
||||
}
|
||||
|
||||
fn maximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
let surface = surface.wl_surface();
|
||||
let seat = &self.common.last_active_seat;
|
||||
let output = active_output(seat, &self.common);
|
||||
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window =
|
||||
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
|
||||
workspace.maximize_request(&window, &output)
|
||||
}
|
||||
}
|
||||
|
||||
fn unmaximize_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
surface.with_pending_state(|state| {
|
||||
state.states.unset(xdg_toplevel::State::Maximized);
|
||||
state.size = None;
|
||||
});
|
||||
surface.send_configure();
|
||||
}
|
||||
|
||||
fn fullscreen_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface, output: Option<WlOutput>) {
|
||||
let output = output
|
||||
.as_ref()
|
||||
.and_then(Output::from_resource)
|
||||
.unwrap_or_else(|| {
|
||||
let seat = &self.common.last_active_seat;
|
||||
active_output(seat, &self.common)
|
||||
});
|
||||
|
||||
let surface = surface.wl_surface();
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window =
|
||||
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
|
||||
workspace.fullscreen_request(&window, &output)
|
||||
}
|
||||
}
|
||||
|
||||
fn unfullscreen_request(&mut self, _dh: &DisplayHandle, surface: ToplevelSurface) {
|
||||
let surface = surface.wl_surface();
|
||||
if let Some(workspace) = self.common.shell.space_for_surface_mut(surface) {
|
||||
let window =
|
||||
workspace.space.window_for_surface(surface, WindowSurfaceType::TOPLEVEL).unwrap().clone();
|
||||
workspace.unfullscreen_request(&window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_grab_preconditions(
|
||||
seat: &Seat<State>,
|
||||
surface: &WlSurface,
|
||||
serial: Serial,
|
||||
) -> Option<PointerGrabStartData> {
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
|
||||
// TODO: touch resize.
|
||||
let pointer = seat.get_pointer().unwrap();
|
||||
|
||||
// Check that this surface has a click grab.
|
||||
if !pointer.has_grab(serial) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start_data = pointer.grab_start_data().unwrap();
|
||||
|
||||
// If the focus was for a different surface, ignore the request.
|
||||
if start_data.focus.is_none()
|
||||
|| !start_data
|
||||
.focus
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.0
|
||||
.id()
|
||||
.same_client_as(&surface.id())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(start_data)
|
||||
}
|
||||
|
||||
|
||||
delegate_xdg_shell!(State);
|
||||
Loading…
Add table
Add a link
Reference in a new issue