mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement wlr-foreign-toplevel-management
The parent event isn't sent but whatever.
This commit is contained in:
@@ -14,6 +14,7 @@ use smithay::input::pointer::{CursorIcon, CursorImageStatus, PointerHandle};
|
|||||||
use smithay::input::{keyboard, Seat, SeatHandler, SeatState};
|
use smithay::input::{keyboard, Seat, SeatHandler, SeatState};
|
||||||
use smithay::output::Output;
|
use smithay::output::Output;
|
||||||
use smithay::reexports::input;
|
use smithay::reexports::input;
|
||||||
|
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||||
use smithay::reexports::wayland_server::protocol::wl_data_source::WlDataSource;
|
use smithay::reexports::wayland_server::protocol::wl_data_source::WlDataSource;
|
||||||
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
|
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
|
||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
@@ -46,7 +47,9 @@ use smithay::{
|
|||||||
delegate_tablet_manager, delegate_text_input_manager, delegate_virtual_keyboard_manager,
|
delegate_tablet_manager, delegate_text_input_manager, delegate_virtual_keyboard_manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::delegate_foreign_toplevel;
|
||||||
use crate::niri::{ClientState, State};
|
use crate::niri::{ClientState, State};
|
||||||
|
use crate::protocols::foreign_toplevel::{ForeignToplevelHandler, ForeignToplevelManagerState};
|
||||||
use crate::utils::output_size;
|
use crate::utils::output_size;
|
||||||
|
|
||||||
impl SeatHandler for State {
|
impl SeatHandler for State {
|
||||||
@@ -291,3 +294,57 @@ impl SecurityContextHandler for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate_security_context!(State);
|
delegate_security_context!(State);
|
||||||
|
|
||||||
|
impl ForeignToplevelHandler for State {
|
||||||
|
fn foreign_toplevel_manager_state(&mut self) -> &mut ForeignToplevelManagerState {
|
||||||
|
&mut self.niri.foreign_toplevel_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate(&mut self, wl_surface: WlSurface) {
|
||||||
|
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||||
|
let window = window.clone();
|
||||||
|
self.niri.layout.activate_window(&window);
|
||||||
|
self.niri.queue_redraw_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, wl_surface: WlSurface) {
|
||||||
|
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||||
|
window.toplevel().send_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) {
|
||||||
|
if let Some((window, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)
|
||||||
|
{
|
||||||
|
if !window
|
||||||
|
.toplevel()
|
||||||
|
.current_state()
|
||||||
|
.capabilities
|
||||||
|
.contains(xdg_toplevel::WmCapabilities::Fullscreen)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = window.clone();
|
||||||
|
|
||||||
|
if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {
|
||||||
|
if &requested_output != current_output {
|
||||||
|
self.niri
|
||||||
|
.layout
|
||||||
|
.move_window_to_output(window.clone(), &requested_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.niri.layout.set_fullscreen(&window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
|
||||||
|
if let Some((window, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||||
|
let window = window.clone();
|
||||||
|
self.niri.layout.set_fullscreen(&window, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate_foreign_toplevel!(State);
|
||||||
|
|||||||
@@ -776,6 +776,27 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
mon.workspaces.iter().flat_map(|ws| ws.windows())
|
mon.workspaces.iter().flat_map(|ws| ws.windows())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_windows(&self, mut f: impl FnMut(&W, Option<&Output>)) {
|
||||||
|
match &self.monitor_set {
|
||||||
|
MonitorSet::Normal { monitors, .. } => {
|
||||||
|
for mon in monitors {
|
||||||
|
for ws in &mon.workspaces {
|
||||||
|
for win in ws.windows() {
|
||||||
|
f(win, Some(&mon.output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MonitorSet::NoOutputs { workspaces } => {
|
||||||
|
for ws in workspaces {
|
||||||
|
for win in ws.windows() {
|
||||||
|
f(win, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn active_monitor(&mut self) -> Option<&mut Monitor<W>> {
|
fn active_monitor(&mut self) -> Option<&mut Monitor<W>> {
|
||||||
let MonitorSet::Normal {
|
let MonitorSet::Normal {
|
||||||
monitors,
|
monitors,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ mod input;
|
|||||||
mod ipc;
|
mod ipc;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod niri;
|
mod niri;
|
||||||
|
mod protocols;
|
||||||
mod render_helpers;
|
mod render_helpers;
|
||||||
mod screenshot_ui;
|
mod screenshot_ui;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ use crate::hotkey_overlay::HotkeyOverlay;
|
|||||||
use crate::input::{apply_libinput_settings, TabletData};
|
use crate::input::{apply_libinput_settings, TabletData};
|
||||||
use crate::ipc::server::IpcServer;
|
use crate::ipc::server::IpcServer;
|
||||||
use crate::layout::{Layout, MonitorRenderElement};
|
use crate::layout::{Layout, MonitorRenderElement};
|
||||||
|
use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState};
|
||||||
use crate::pw_utils::{Cast, PipeWire};
|
use crate::pw_utils::{Cast, PipeWire};
|
||||||
use crate::render_helpers::NiriRenderer;
|
use crate::render_helpers::NiriRenderer;
|
||||||
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
use crate::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement};
|
||||||
@@ -151,6 +152,7 @@ pub struct Niri {
|
|||||||
pub kde_decoration_state: KdeDecorationState,
|
pub kde_decoration_state: KdeDecorationState,
|
||||||
pub layer_shell_state: WlrLayerShellState,
|
pub layer_shell_state: WlrLayerShellState,
|
||||||
pub session_lock_state: SessionLockManagerState,
|
pub session_lock_state: SessionLockManagerState,
|
||||||
|
pub foreign_toplevel_state: ForeignToplevelManagerState,
|
||||||
pub shm_state: ShmState,
|
pub shm_state: ShmState,
|
||||||
pub output_manager_state: OutputManagerState,
|
pub output_manager_state: OutputManagerState,
|
||||||
pub dmabuf_state: DmabufState,
|
pub dmabuf_state: DmabufState,
|
||||||
@@ -327,6 +329,7 @@ impl State {
|
|||||||
self.refresh_popup_grab();
|
self.refresh_popup_grab();
|
||||||
self.update_keyboard_focus();
|
self.update_keyboard_focus();
|
||||||
self.refresh_pointer_focus();
|
self.refresh_pointer_focus();
|
||||||
|
foreign_toplevel::refresh(self);
|
||||||
|
|
||||||
{
|
{
|
||||||
let _span = tracy_client::span!("flush_clients");
|
let _span = tracy_client::span!("flush_clients");
|
||||||
@@ -855,6 +858,11 @@ impl Niri {
|
|||||||
!client.get_data::<ClientState>().unwrap().restricted
|
!client.get_data::<ClientState>().unwrap().restricted
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let foreign_toplevel_state =
|
||||||
|
ForeignToplevelManagerState::new::<State, _>(&display_handle, |client| {
|
||||||
|
!client.get_data::<ClientState>().unwrap().restricted
|
||||||
|
});
|
||||||
|
|
||||||
let mut seat: Seat<State> = seat_state.new_wl_seat(&display_handle, backend.seat_name());
|
let mut seat: Seat<State> = seat_state.new_wl_seat(&display_handle, backend.seat_name());
|
||||||
seat.add_keyboard(
|
seat.add_keyboard(
|
||||||
config_.input.keyboard.xkb.to_xkb_config(),
|
config_.input.keyboard.xkb.to_xkb_config(),
|
||||||
@@ -972,6 +980,7 @@ impl Niri {
|
|||||||
kde_decoration_state,
|
kde_decoration_state,
|
||||||
layer_shell_state,
|
layer_shell_state,
|
||||||
session_lock_state,
|
session_lock_state,
|
||||||
|
foreign_toplevel_state,
|
||||||
text_input_state,
|
text_input_state,
|
||||||
input_method_state,
|
input_method_state,
|
||||||
virtual_keyboard_state,
|
virtual_keyboard_state,
|
||||||
|
|||||||
@@ -0,0 +1,417 @@
|
|||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use smithay::output::Output;
|
||||||
|
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||||
|
use smithay::reexports::wayland_protocols_wlr;
|
||||||
|
use smithay::reexports::wayland_server::backend::ClientId;
|
||||||
|
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
|
||||||
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
|
use smithay::reexports::wayland_server::{
|
||||||
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
||||||
|
};
|
||||||
|
use smithay::wayland::compositor::with_states;
|
||||||
|
use smithay::wayland::shell::xdg::{ToplevelStateSet, XdgToplevelSurfaceData};
|
||||||
|
use wayland_protocols_wlr::foreign_toplevel::v1::server::{
|
||||||
|
zwlr_foreign_toplevel_handle_v1, zwlr_foreign_toplevel_manager_v1,
|
||||||
|
};
|
||||||
|
use zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1;
|
||||||
|
use zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1;
|
||||||
|
|
||||||
|
use crate::niri::State;
|
||||||
|
|
||||||
|
const VERSION: u32 = 3;
|
||||||
|
|
||||||
|
pub struct ForeignToplevelManagerState {
|
||||||
|
display: DisplayHandle,
|
||||||
|
instances: Vec<ZwlrForeignToplevelManagerV1>,
|
||||||
|
toplevels: HashMap<WlSurface, ToplevelData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ForeignToplevelHandler {
|
||||||
|
fn foreign_toplevel_manager_state(&mut self) -> &mut ForeignToplevelManagerState;
|
||||||
|
fn activate(&mut self, wl_surface: WlSurface);
|
||||||
|
fn close(&mut self, wl_surface: WlSurface);
|
||||||
|
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>);
|
||||||
|
fn unset_fullscreen(&mut self, wl_surface: WlSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ToplevelData {
|
||||||
|
title: Option<String>,
|
||||||
|
app_id: Option<String>,
|
||||||
|
states: ArrayVec<u32, 3>,
|
||||||
|
output: Option<Output>,
|
||||||
|
instances: HashMap<ZwlrForeignToplevelHandleV1, Vec<WlOutput>>,
|
||||||
|
// FIXME: parent.
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ForeignToplevelGlobalData {
|
||||||
|
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForeignToplevelManagerState {
|
||||||
|
pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData>,
|
||||||
|
D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
|
||||||
|
D: 'static,
|
||||||
|
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let global_data = ForeignToplevelGlobalData {
|
||||||
|
filter: Box::new(filter),
|
||||||
|
};
|
||||||
|
display.create_global::<D, ZwlrForeignToplevelManagerV1, _>(VERSION, global_data);
|
||||||
|
Self {
|
||||||
|
display: display.clone(),
|
||||||
|
instances: Vec::new(),
|
||||||
|
toplevels: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(state: &mut State) {
|
||||||
|
let _span = tracy_client::span!("foreign_toplevel::refresh");
|
||||||
|
|
||||||
|
let protocol_state = &mut state.niri.foreign_toplevel_state;
|
||||||
|
|
||||||
|
// Handle closed windows.
|
||||||
|
protocol_state.toplevels.retain(|surface, data| {
|
||||||
|
if state.niri.layout.find_window_and_output(surface).is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for instance in data.instances.keys() {
|
||||||
|
instance.closed();
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle new and existing windows.
|
||||||
|
state.niri.layout.with_windows(|window, output| {
|
||||||
|
let wl_surface = window.toplevel().wl_surface();
|
||||||
|
|
||||||
|
with_states(wl_surface, |states| {
|
||||||
|
let role = states
|
||||||
|
.data_map
|
||||||
|
.get::<XdgToplevelSurfaceData>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let states = foreign_toplevel_state(&role.current.states);
|
||||||
|
|
||||||
|
match protocol_state.toplevels.entry(wl_surface.clone()) {
|
||||||
|
Entry::Occupied(entry) => {
|
||||||
|
// Existing window, check if anything changed.
|
||||||
|
let data = entry.into_mut();
|
||||||
|
|
||||||
|
let mut new_title = None;
|
||||||
|
if data.title != role.title {
|
||||||
|
data.title = role.title.clone();
|
||||||
|
new_title = role.title.as_deref();
|
||||||
|
|
||||||
|
if new_title.is_none() {
|
||||||
|
error!("toplevel title changed to None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_app_id = None;
|
||||||
|
if data.app_id != role.app_id {
|
||||||
|
data.app_id = role.app_id.clone();
|
||||||
|
new_app_id = role.app_id.as_deref();
|
||||||
|
|
||||||
|
if new_app_id.is_none() {
|
||||||
|
error!("toplevel app_id changed to None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut states_changed = false;
|
||||||
|
if data.states != states {
|
||||||
|
data.states = states;
|
||||||
|
states_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output_changed = false;
|
||||||
|
if data.output.as_ref() != output {
|
||||||
|
data.output = output.cloned();
|
||||||
|
output_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let something_changed = new_title.is_some()
|
||||||
|
|| new_app_id.is_some()
|
||||||
|
|| states_changed
|
||||||
|
|| output_changed;
|
||||||
|
|
||||||
|
if something_changed {
|
||||||
|
for (instance, outputs) in &mut data.instances {
|
||||||
|
if let Some(new_title) = new_title {
|
||||||
|
instance.title(new_title.to_owned());
|
||||||
|
}
|
||||||
|
if let Some(new_app_id) = new_app_id {
|
||||||
|
instance.app_id(new_app_id.to_owned());
|
||||||
|
}
|
||||||
|
if states_changed {
|
||||||
|
instance.state(
|
||||||
|
data.states.iter().flat_map(|x| x.to_ne_bytes()).collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if output_changed {
|
||||||
|
for wl_output in outputs.drain(..) {
|
||||||
|
instance.output_leave(&wl_output);
|
||||||
|
}
|
||||||
|
if let Some(output) = &data.output {
|
||||||
|
if let Some(client) = instance.client() {
|
||||||
|
for wl_output in output.client_outputs(&client) {
|
||||||
|
instance.output_enter(&wl_output);
|
||||||
|
outputs.push(wl_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (instance, outputs) in &mut data.instances {
|
||||||
|
// Clean up dead wl_outputs.
|
||||||
|
outputs.retain(|x| x.is_alive());
|
||||||
|
|
||||||
|
let mut send_done = something_changed;
|
||||||
|
|
||||||
|
// If the client bound any more wl_outputs, send enter for them.
|
||||||
|
if let Some(output) = &data.output {
|
||||||
|
if let Some(client) = instance.client() {
|
||||||
|
for wl_output in output.client_outputs(&client) {
|
||||||
|
if !outputs.iter().any(|x| x == &wl_output) {
|
||||||
|
instance.output_enter(&wl_output);
|
||||||
|
outputs.push(wl_output);
|
||||||
|
send_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if send_done {
|
||||||
|
instance.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
// New window, start tracking it.
|
||||||
|
let mut data = ToplevelData {
|
||||||
|
title: role.title.clone(),
|
||||||
|
app_id: role.app_id.clone(),
|
||||||
|
states,
|
||||||
|
output: output.cloned(),
|
||||||
|
instances: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for manager in &protocol_state.instances {
|
||||||
|
if let Some(client) = manager.client() {
|
||||||
|
data.add_instance::<State>(&protocol_state.display, &client, manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.insert(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToplevelData {
|
||||||
|
fn add_instance<D>(
|
||||||
|
&mut self,
|
||||||
|
handle: &DisplayHandle,
|
||||||
|
client: &Client,
|
||||||
|
manager: &ZwlrForeignToplevelManagerV1,
|
||||||
|
) where
|
||||||
|
D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,
|
||||||
|
D: 'static,
|
||||||
|
{
|
||||||
|
let toplevel = client
|
||||||
|
.create_resource::<ZwlrForeignToplevelHandleV1, _, D>(handle, manager.version(), ())
|
||||||
|
.unwrap();
|
||||||
|
manager.toplevel(&toplevel);
|
||||||
|
|
||||||
|
if let Some(title) = &self.title {
|
||||||
|
toplevel.title(title.clone());
|
||||||
|
}
|
||||||
|
if let Some(app_id) = &self.app_id {
|
||||||
|
toplevel.app_id(app_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
toplevel.state(self.states.iter().flat_map(|x| x.to_ne_bytes()).collect());
|
||||||
|
|
||||||
|
let mut outputs = Vec::new();
|
||||||
|
if let Some(output) = &self.output {
|
||||||
|
for wl_output in output.client_outputs(client) {
|
||||||
|
toplevel.output_enter(&wl_output);
|
||||||
|
outputs.push(wl_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toplevel.done();
|
||||||
|
|
||||||
|
self.instances.insert(toplevel, outputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData, D>
|
||||||
|
for ForeignToplevelManagerState
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData>,
|
||||||
|
D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
|
||||||
|
D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,
|
||||||
|
D: ForeignToplevelHandler,
|
||||||
|
{
|
||||||
|
fn bind(
|
||||||
|
state: &mut D,
|
||||||
|
handle: &DisplayHandle,
|
||||||
|
client: &Client,
|
||||||
|
resource: New<ZwlrForeignToplevelManagerV1>,
|
||||||
|
_global_data: &ForeignToplevelGlobalData,
|
||||||
|
data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
let manager = data_init.init(resource, ());
|
||||||
|
|
||||||
|
let state = state.foreign_toplevel_manager_state();
|
||||||
|
|
||||||
|
for data in state.toplevels.values_mut() {
|
||||||
|
data.add_instance::<D>(handle, client, &manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.instances.push(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_view(client: Client, global_data: &ForeignToplevelGlobalData) -> bool {
|
||||||
|
(global_data.filter)(&client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Dispatch<ZwlrForeignToplevelManagerV1, (), D> for ForeignToplevelManagerState
|
||||||
|
where
|
||||||
|
D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
|
||||||
|
D: ForeignToplevelHandler,
|
||||||
|
{
|
||||||
|
fn request(
|
||||||
|
state: &mut D,
|
||||||
|
_client: &Client,
|
||||||
|
resource: &ZwlrForeignToplevelManagerV1,
|
||||||
|
request: <ZwlrForeignToplevelManagerV1 as Resource>::Request,
|
||||||
|
_data: &(),
|
||||||
|
_dhandle: &DisplayHandle,
|
||||||
|
_data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
zwlr_foreign_toplevel_manager_v1::Request::Stop => {
|
||||||
|
resource.finished();
|
||||||
|
|
||||||
|
let state = state.foreign_toplevel_manager_state();
|
||||||
|
state.instances.retain(|x| x != resource);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroyed(
|
||||||
|
state: &mut D,
|
||||||
|
_client: ClientId,
|
||||||
|
resource: &ZwlrForeignToplevelManagerV1,
|
||||||
|
_data: &(),
|
||||||
|
) {
|
||||||
|
let state = state.foreign_toplevel_manager_state();
|
||||||
|
state.instances.retain(|x| x != resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Dispatch<ZwlrForeignToplevelHandleV1, (), D> for ForeignToplevelManagerState
|
||||||
|
where
|
||||||
|
D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,
|
||||||
|
D: ForeignToplevelHandler,
|
||||||
|
{
|
||||||
|
fn request(
|
||||||
|
state: &mut D,
|
||||||
|
_client: &Client,
|
||||||
|
resource: &ZwlrForeignToplevelHandleV1,
|
||||||
|
request: <ZwlrForeignToplevelHandleV1 as Resource>::Request,
|
||||||
|
_data: &(),
|
||||||
|
_dhandle: &DisplayHandle,
|
||||||
|
_data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
let protocol_state = state.foreign_toplevel_manager_state();
|
||||||
|
|
||||||
|
let Some((surface, _)) = protocol_state
|
||||||
|
.toplevels
|
||||||
|
.iter()
|
||||||
|
.find(|(_, data)| data.instances.contains_key(resource))
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let surface = surface.clone();
|
||||||
|
|
||||||
|
match request {
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetMinimized => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetMinimized => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::Activate { .. } => {
|
||||||
|
state.activate(surface);
|
||||||
|
}
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::Close => {
|
||||||
|
state.close(surface);
|
||||||
|
}
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetRectangle { .. } => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::Destroy => (),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetFullscreen { output } => {
|
||||||
|
state.set_fullscreen(surface, output);
|
||||||
|
}
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetFullscreen => {
|
||||||
|
state.unset_fullscreen(surface);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroyed(
|
||||||
|
state: &mut D,
|
||||||
|
_client: ClientId,
|
||||||
|
resource: &ZwlrForeignToplevelHandleV1,
|
||||||
|
_data: &(),
|
||||||
|
) {
|
||||||
|
let state = state.foreign_toplevel_manager_state();
|
||||||
|
for data in state.toplevels.values_mut() {
|
||||||
|
data.instances.retain(|instance, _| instance != resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foreign_toplevel_state(states: &ToplevelStateSet) -> ArrayVec<u32, 3> {
|
||||||
|
let mut rv = ArrayVec::new();
|
||||||
|
if states.contains(xdg_toplevel::State::Maximized) {
|
||||||
|
rv.push(zwlr_foreign_toplevel_handle_v1::State::Maximized as u32);
|
||||||
|
}
|
||||||
|
if states.contains(xdg_toplevel::State::Activated) {
|
||||||
|
rv.push(zwlr_foreign_toplevel_handle_v1::State::Activated as u32);
|
||||||
|
}
|
||||||
|
if states.contains(xdg_toplevel::State::Fullscreen) {
|
||||||
|
rv.push(zwlr_foreign_toplevel_handle_v1::State::Fullscreen as u32);
|
||||||
|
}
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! delegate_foreign_toplevel {
|
||||||
|
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
|
||||||
|
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: $crate::protocols::foreign_toplevel::ForeignToplevelGlobalData
|
||||||
|
] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);
|
||||||
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: ()
|
||||||
|
] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);
|
||||||
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1: ()
|
||||||
|
] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod foreign_toplevel;
|
||||||
Reference in New Issue
Block a user