1
0
Fork 0
forked from wry/wry
wry/src/xwayland/xwm.rs

1672 lines
58 KiB
Rust

#![allow(clippy::await_holding_refcell_ref)] // all borrows are to data that is only used by this task
mod selection;
mod properties;
mod transfer;
use jay_geometry::Rect;
use jay_async_engine::SpawnedFuture;
use selection::SelectionData;
use {
crate::{
client::Client,
ifs::{
data_transfer::{
DataOfferId, DataSourceId, DynDataOffer, DynDataSource, TransferLocation, TransferVtable,
SourceData, add_data_source_mime_type, destroy_data_device, destroy_data_offer,
destroy_data_source, receive_data_offer,
x_data_device::{XClipboardTransfer, XTransfer, XTransferDevice, XPrimarySelectionTransfer},
x_data_offer::XDataOffer,
x_data_source::XDataSource,
},
wl_seat::{SeatId, WlSeatGlobal},
wl_surface::{
WlSurface,
x_surface::xwindow::{Xwindow, XwindowData},
},
},
state::State,
tree::{Node, ToplevelNode},
utils::{
bitflags::BitflagsExt,
buf::Buf,
cell_ext::CellExt,
clonecell::CloneCell,
copyhashmap::CopyHashMap,
errorfmt::ErrorFmt,
hash_map_ext::HashMapExt,
linkedlist::LinkedList,
numcell::NumCell,
pipe::{Pipe, pipe},
rc_eq::rc_eq,
},
wire::WlSurfaceId,
wire_xcon::{
ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows,
ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues,
ConvertSelection, CreateNotify, CreateWindow, CreateWindowValues, DestroyNotify,
Extension, FocusIn, GetGeometry, InternAtom, KillClient, MapNotify,
MapRequest, MapWindow, PropertyNotify, ResClientIdSpec, ResQueryClientIds,
SelectSelectionInput, SelectionNotify, SelectionRequest, SetInputFocus,
SetSelectionOwner, UnmapNotify, XfixesQueryVersion, XfixesSelectionNotify,
},
xcon::{
Event, XEvent, Xcon, XconError,
consts::{
_NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE, ATOM_ATOM,
ATOM_NONE, ATOM_STRING, ATOM_WINDOW, ATOM_WM_CLASS, ATOM_WM_NAME,
ATOM_WM_TRANSIENT_FOR, COMPOSITE_REDIRECT_MANUAL, CONFIG_WINDOW_HEIGHT,
CONFIG_WINDOW_WIDTH, CONFIG_WINDOW_X, CONFIG_WINDOW_Y, EVENT_MASK_FOCUS_CHANGE,
EVENT_MASK_PROPERTY_CHANGE, EVENT_MASK_SUBSTRUCTURE_NOTIFY,
EVENT_MASK_SUBSTRUCTURE_REDIRECT, ICCCM_WM_STATE_ICONIC, ICCCM_WM_STATE_NORMAL,
ICCCM_WM_STATE_WITHDRAWN, INPUT_FOCUS_POINTER_ROOT, NOTIFY_DETAIL_POINTER,
NOTIFY_MODE_GRAB, NOTIFY_MODE_UNGRAB, PROP_MODE_REPLACE,
RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID, SELECTION_CLIENT_CLOSE_MASK,
SELECTION_WINDOW_DESTROY_MASK, SET_SELECTION_OWNER_MASK, STACK_MODE_ABOVE,
STACK_MODE_BELOW, WINDOW_CLASS_INPUT_OUTPUT,
},
},
xwayland::{XWaylandError, XWaylandEvent},
},
ahash::{AHashMap, AHashSet},
bstr::ByteSlice,
futures_util::{FutureExt, select},
smallvec::SmallVec,
std::{
borrow::Cow,
cell::{Cell, RefCell},
marker::PhantomData,
mem::{self},
ops::{Deref, DerefMut},
rc::Rc,
},
uapi::OwnedFd,
};
atoms! {
Atoms;
CLIPBOARD,
CLIPBOARD_MANAGER,
COMPOUND_TEXT,
DELETE,
INCR,
_MOTIF_WM_HINTS,
_NET_ACTIVE_WINDOW,
_NET_CLIENT_LIST,
_NET_CLIENT_LIST_STACKING,
_NET_STARTUP_ID,
_NET_STARTUP_INFO,
_NET_STARTUP_INFO_BEGIN,
_NET_SUPPORTED,
_NET_SUPPORTING_WM_CHECK,
_NET_WM_CM_S0,
_NET_WM_MOVERESIZE,
_NET_WM_NAME,
_NET_WM_PID,
_NET_WM_PING,
_NET_WM_STATE,
_NET_WM_STATE_FOCUSED,
_NET_WM_STATE_FULLSCREEN,
_NET_WM_STATE_HIDDEN,
_NET_WM_STATE_MAXIMIZED_HORZ,
_NET_WM_STATE_MAXIMIZED_VERT,
_NET_WM_STATE_MODAL,
_NET_WM_WINDOW_TYPE,
_NET_WM_WINDOW_TYPE_COMBO,
_NET_WM_WINDOW_TYPE_DIALOG,
_NET_WM_WINDOW_TYPE_DND,
_NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
_NET_WM_WINDOW_TYPE_MENU,
_NET_WM_WINDOW_TYPE_NORMAL,
_NET_WM_WINDOW_TYPE_NOTIFICATION,
_NET_WM_WINDOW_TYPE_POPUP_MENU,
_NET_WM_WINDOW_TYPE_SPLASH,
_NET_WM_WINDOW_TYPE_TOOLBAR,
_NET_WM_WINDOW_TYPE_TOOLTIP,
_NET_WM_WINDOW_TYPE_UTILITY,
PRIMARY,
TARGETS,
TEXT,
TIMESTAMP,
UTF8_STRING,
WINDOW,
_WL_SELECTION,
WL_SURFACE_ID,
WL_SURFACE_SERIAL,
WM_CHANGE_STATE,
WM_DELETE_WINDOW,
WM_HINTS,
WM_NORMAL_HINTS,
WM_PROTOCOLS,
WM_S0,
WM_SIZE_HINTS,
WM_STATE,
WM_TAKE_FOCUS,
WM_WINDOW_ROLE,
XdndActionAsk,
XdndActionCopy,
XdndActionMove,
XdndActionPrivate,
XdndAware,
XdndDrop,
XdndEnter,
XdndFinished,
XdndLeave,
XdndPosition,
XdndProxy,
XdndSelection,
XdndStatus,
XdndTypeList,
}
#[derive(Default)]
pub struct XwmShared {
devices: CopyHashMap<SeatId, Rc<XTransferDevice>>,
data: SelectionData<XClipboardTransfer>,
primary_selection: SelectionData<XPrimarySelectionTransfer>,
transfers: CopyHashMap<u64, SpawnedFuture<()>>,
}
impl Drop for XwmShared {
fn drop(&mut self) {
self.data.destroy();
self.primary_selection.destroy();
for device in self.devices.lock().drain_values() {
destroy_data_device::<XClipboardTransfer>(&device);
destroy_data_device::<XPrimarySelectionTransfer>(&device);
device.seat.unset_x_data_device(device.id);
}
self.transfers.clear();
}
}
pub struct Wm {
state: Rc<State>,
c: Rc<Xcon>,
atoms: Atoms,
never_focus: AHashSet<u32>,
root: u32,
xwin: u32,
client: Rc<Client>,
windows: AHashMap<u32, Rc<XwindowData>>,
windows_by_surface_id: AHashMap<WlSurfaceId, Rc<XwindowData>>,
windows_by_surface_serial: AHashMap<u64, Rc<XwindowData>>,
last_surface_serial: u64,
focus_window: Option<Rc<XwindowData>>,
last_input_serial: u64,
atom_cache: AHashMap<String, u32>,
atom_name_cache: AHashMap<u32, String>,
transfer_ids: NumCell<u64>,
known_seats: AHashMap<SeatId, Rc<WlSeatGlobal>>,
shared: Rc<XwmShared>,
stack_list: LinkedList<Rc<XwindowData>>,
num_stacked: usize,
map_list: LinkedList<Rc<XwindowData>>,
num_mapped: usize,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Initiator {
X,
Wayland,
}
impl Drop for Wm {
fn drop(&mut self) {
for window in self.windows.drain_values() {
if let Some(window) = window.window.take() {
window.break_loops();
}
window.children.clear();
window.parent.take();
window.stack_link.take();
window.map_link.take();
}
self.windows_by_surface_id.clear();
self.windows_by_surface_serial.clear();
self.focus_window.take();
self.known_seats.clear();
}
}
impl Wm {
pub(super) async fn get(
state: &Rc<State>,
client: Rc<Client>,
socket: OwnedFd,
shared: &Rc<XwmShared>,
) -> Result<Self, XWaylandError> {
let c = match Xcon::connect_to_fd(state, &Rc::new(socket), &[], &[]).await {
Ok(c) => c,
Err(e) => return Err(XWaylandError::Connect(e)),
};
let atoms = match Atoms::get(&c).await {
Ok(a) => a,
Err(e) => return Err(XWaylandError::LoadAtoms(e)),
};
let never_focus = {
let mut nf = AHashSet::new();
nf.insert(atoms._NET_WM_WINDOW_TYPE_COMBO);
nf.insert(atoms._NET_WM_WINDOW_TYPE_DND);
nf.insert(atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU);
nf.insert(atoms._NET_WM_WINDOW_TYPE_MENU);
nf.insert(atoms._NET_WM_WINDOW_TYPE_NOTIFICATION);
nf.insert(atoms._NET_WM_WINDOW_TYPE_POPUP_MENU);
nf.insert(atoms._NET_WM_WINDOW_TYPE_SPLASH);
nf.insert(atoms._NET_WM_WINDOW_TYPE_TOOLTIP);
nf.insert(atoms._NET_WM_WINDOW_TYPE_UTILITY);
nf
};
let root = c.root_window();
{
let events = 0
| EVENT_MASK_SUBSTRUCTURE_NOTIFY
| EVENT_MASK_SUBSTRUCTURE_REDIRECT
| EVENT_MASK_PROPERTY_CHANGE;
let cwa = ChangeWindowAttributes {
window: root,
values: CreateWindowValues {
event_mask: Some(events),
..Default::default()
},
};
if let Err(e) = c.call(&cwa).await {
return Err(XWaylandError::SelectRootEvents(e));
}
}
{
let crs = CompositeRedirectSubwindows {
window: root,
update: COMPOSITE_REDIRECT_MANUAL,
};
if let Err(e) = c.call(&crs).await {
return Err(XWaylandError::CompositeRedirectSubwindows(e));
}
}
let xwin = {
let xwin = c.generate_id()?;
let cw = CreateWindow {
depth: 0,
wid: xwin,
parent: root,
x: 0,
y: 0,
width: 10,
height: 10,
border_width: 0,
class: WINDOW_CLASS_INPUT_OUTPUT,
visual: 0,
values: Default::default(),
};
if let Err(e) = c.call(&cw).await {
return Err(XWaylandError::CreateXWindow(e));
}
c.call(&ChangeProperty {
mode: PROP_MODE_REPLACE,
window: xwin,
property: atoms._NET_WM_NAME,
ty: atoms.UTF8_STRING,
format: 8,
data: "jay wm".as_bytes(),
});
c.call(&ChangeProperty {
mode: PROP_MODE_REPLACE,
window: root,
property: atoms._NET_SUPPORTING_WM_CHECK,
ty: ATOM_WINDOW,
format: 32,
data: uapi::as_bytes(&xwin),
});
c.call(&ChangeProperty {
mode: PROP_MODE_REPLACE,
window: xwin,
property: atoms._NET_SUPPORTING_WM_CHECK,
ty: ATOM_WINDOW,
format: 32,
data: uapi::as_bytes(&xwin),
});
c.call(&SetSelectionOwner {
owner: xwin,
selection: atoms.WM_S0,
time: 0,
});
c.call(&SetSelectionOwner {
owner: xwin,
selection: atoms._NET_WM_CM_S0,
time: 0,
});
xwin
};
{
let supported_atoms = [
atoms._NET_WM_STATE,
atoms._NET_ACTIVE_WINDOW,
atoms._NET_WM_MOVERESIZE,
atoms._NET_WM_STATE_FOCUSED,
atoms._NET_WM_STATE_MODAL,
atoms._NET_WM_STATE_FULLSCREEN,
atoms._NET_WM_STATE_MAXIMIZED_VERT,
atoms._NET_WM_STATE_MAXIMIZED_HORZ,
atoms._NET_WM_STATE_HIDDEN,
atoms._NET_CLIENT_LIST,
atoms._NET_CLIENT_LIST_STACKING,
];
c.call(&ChangeProperty {
mode: PROP_MODE_REPLACE,
window: root,
property: atoms._NET_SUPPORTED,
ty: ATOM_ATOM,
format: 32,
data: uapi::as_bytes(&supported_atoms[..]),
});
}
{
c.call(&ChangeProperty {
mode: PROP_MODE_REPLACE,
window: root,
property: atoms._NET_ACTIVE_WINDOW,
ty: ATOM_ATOM,
format: 32,
data: uapi::as_bytes(&0u32),
});
}
'set_root_cursor: {
let cursors = match state.cursors.get() {
Some(g) => g,
_ => break 'set_root_cursor,
};
let first = match cursors.default.xcursor.first() {
Some(f) => f,
_ => break 'set_root_cursor,
};
let first = match first.iter().find(|i| i.0.0 == 1) {
Some(f) => f.1,
_ => break 'set_root_cursor,
};
let cursor = match c
.create_cursor(
&first.pixels,
first.width,
first.height,
first.xhot,
first.yhot,
)
.await
{
Ok(c) => c,
Err(e) => {
log::warn!("Could not create a root cursor: {}", ErrorFmt(e));
break 'set_root_cursor;
}
};
let cwa = ChangeWindowAttributes {
window: root,
values: CreateWindowValues {
cursor: Some(cursor),
..Default::default()
},
};
if let Err(e) = c.call(&cwa).await {
return Err(XWaylandError::SetCursor(e));
}
}
{
let qv = XfixesQueryVersion {
client_major_version: 1,
client_minor_version: 0,
};
if let Err(e) = c.call(&qv).await {
return Err(XWaylandError::XfixesQueryVersion(e));
}
}
let mut clipboard_wins = [0, 0];
for (idx, atom) in [atoms.CLIPBOARD, atoms.PRIMARY].into_iter().enumerate() {
let win = c.generate_id()?;
let cw = CreateWindow {
depth: 0,
wid: win,
parent: root,
x: 0,
y: 0,
width: 10,
height: 10,
border_width: 0,
class: WINDOW_CLASS_INPUT_OUTPUT,
visual: 0,
values: CreateWindowValues {
event_mask: None,
..Default::default()
},
};
if let Err(e) = c.call(&cw).await {
return Err(XWaylandError::CreateSelectionWindow(e));
}
let ssi = SelectSelectionInput {
window: win,
selection: atom,
event_mask: SET_SELECTION_OWNER_MASK
| SELECTION_CLIENT_CLOSE_MASK
| SELECTION_WINDOW_DESTROY_MASK,
};
if let Err(e) = c.call(&ssi).await {
return Err(XWaylandError::WatchSelection(e));
}
clipboard_wins[idx] = win;
}
shared.data.win.set(clipboard_wins[0]);
shared.data.selection.set(atoms.CLIPBOARD);
shared.primary_selection.win.set(clipboard_wins[1]);
shared.primary_selection.selection.set(atoms.PRIMARY);
Ok(Self {
state: state.clone(),
c,
atoms,
never_focus,
root,
xwin,
client,
windows: Default::default(),
windows_by_surface_id: Default::default(),
windows_by_surface_serial: Default::default(),
last_surface_serial: 0,
focus_window: Default::default(),
last_input_serial: 0,
atom_cache: Default::default(),
atom_name_cache: Default::default(),
transfer_ids: Default::default(),
known_seats: Default::default(),
shared: shared.clone(),
stack_list: Default::default(),
num_stacked: 0,
map_list: Default::default(),
num_mapped: 0,
})
}
fn seats_changed(&mut self) {
let current_seats: AHashMap<_, _> = self
.state
.globals
.seats
.lock()
.values()
.map(|s| (s.id(), s.clone()))
.collect();
let mut new_seats = vec![];
let mut removed_seats = vec![];
for (id, seat) in &current_seats {
if !self.known_seats.contains_key(id) {
new_seats.push(seat.clone());
}
}
for id in self.known_seats.keys() {
if !current_seats.contains_key(id) {
removed_seats.push(*id);
}
}
for seat in removed_seats {
self.shared.data.seat_removed(seat);
self.shared.primary_selection.seat_removed(seat);
self.shared.devices.remove(&seat);
}
for seat in new_seats {
let dd = Rc::new(XTransferDevice {
id: self.state.xwayland.ipc_device_ids.next(),
clipboard: Default::default(),
primary_selection: Default::default(),
seat: seat.clone(),
state: self.state.clone(),
client: self.client.clone(),
});
seat.set_x_data_device(&dd);
self.shared.devices.set(seat.id(), dd.clone());
}
self.known_seats = current_seats;
}
pub async fn run(mut self) {
self.seats_changed();
loop {
select! {
e = self.state.xwayland.queue.pop().fuse() => self.handle_xwayland_event(e).await,
e = self.c.event().fuse() => self.handle_event(&e).await,
}
}
}
async fn handle_xwayland_event(&mut self, e: XWaylandEvent) {
match e {
XWaylandEvent::SurfaceCreated(event) => {
self.handle_xwayland_surface_created(event).await
}
XWaylandEvent::SurfaceSerialAssigned(event) => {
self.handle_xwayland_surface_serial_assigned(event).await
}
XWaylandEvent::Configure(event) => self.handle_xwayland_configure(event).await,
XWaylandEvent::SurfaceDestroyed(surface_id, serial) => {
self.handle_xwayland_surface_destroyed(surface_id, serial)
}
XWaylandEvent::Activate(window) => {
self.activate_window(Some(&window), Initiator::Wayland)
.await
}
XWaylandEvent::ActivateRoot => self.activate_window(None, Initiator::Wayland).await,
XWaylandEvent::Close(window) => self.close_window(&window).await,
XWaylandEvent::SeatChanged => self.seats_changed(),
XWaylandEvent::DataTransferCancelSource {
location,
seat,
source,
} => match location {
TransferLocation::Clipboard => {
self.dd_cancel_source::<XClipboardTransfer>(&self.shared.clone().data, seat, source)
}
TransferLocation::PrimarySelection => self.dd_cancel_source::<XPrimarySelectionTransfer>(
&self.shared.clone().primary_selection,
seat,
source,
),
},
XWaylandEvent::DataTransferSendSource {
location,
seat,
source,
mime_type,
fd,
} => match location {
TransferLocation::Clipboard => {
self.dd_send_source::<XClipboardTransfer>(
&self.shared.clone().data,
seat,
source,
mime_type,
fd,
)
.await
}
TransferLocation::PrimarySelection => {
self.dd_send_source::<XPrimarySelectionTransfer>(
&self.shared.clone().primary_selection,
seat,
source,
mime_type,
fd,
)
.await
}
},
XWaylandEvent::DataTransferSetOffer {
location,
seat,
offer,
} => match location {
TransferLocation::Clipboard => {
self.dd_set_offer::<XClipboardTransfer>(&self.shared.clone().data, seat, offer)
.await
}
TransferLocation::PrimarySelection => {
self.dd_set_offer::<XPrimarySelectionTransfer>(
&self.shared.clone().primary_selection,
seat,
offer,
)
.await
}
},
XWaylandEvent::DataTransferSetSelection {
seat,
location,
offer,
} => match location {
TransferLocation::Clipboard => {
self.dd_set_selection::<XClipboardTransfer>(&self.shared.clone().data, seat, offer)
.await
}
TransferLocation::PrimarySelection => {
self.dd_set_selection::<XPrimarySelectionTransfer>(
&self.shared.clone().primary_selection,
seat,
offer,
)
.await
}
},
XWaylandEvent::DataTransferAddOfferMimeType {
location,
seat,
offer,
mime_type,
} => match location {
TransferLocation::Clipboard => {
self.dd_add_offer_mime_type::<XClipboardTransfer>(
&self.shared.clone().data,
seat,
offer,
mime_type,
)
.await
}
TransferLocation::PrimarySelection => {
self.dd_add_offer_mime_type::<XPrimarySelectionTransfer>(
&self.shared.clone().primary_selection,
seat,
offer,
mime_type,
)
.await
}
},
}
}
async fn handle_xwayland_configure(&mut self, window: Rc<Xwindow>) {
if window.data.destroyed.get() {
return;
}
self.send_configure(window).await;
}
async fn send_configure(&mut self, window: Rc<Xwindow>) {
let extents = window.data.info.extents.get();
// log::info!("xwin {} send_configure {:?}", window.data.window_id, extents);
let mut x = extents.x1();
let mut y = extents.y1();
let mut width = extents.width();
let mut height = extents.height();
logical_to_client_wire_scale!(self.client, x, y, width, height);
let cw = ConfigureWindow {
window: window.data.window_id,
values: ConfigureWindowValues {
x: Some(x),
y: Some(y),
width: Some(width as u32),
height: Some(height as u32),
border_width: Some(0),
..Default::default()
},
};
if let Err(e) = self.c.call(&cw).await {
log::error!("Could not configure window: {}", ErrorFmt(e));
}
}
async fn set_minimized(&self, data: &Rc<XwindowData>, minimized: bool) {
data.info.minimized.set(minimized);
let state = match minimized {
true => ICCCM_WM_STATE_ICONIC,
false => ICCCM_WM_STATE_NORMAL,
};
self.set_wm_state(data, state).await;
self.set_net_wm_state(data).await;
}
#[expect(dead_code)]
async fn set_maximized(&self, data: &Rc<XwindowData>, maximized: bool) {
data.info.maximized_vert.set(maximized);
data.info.maximized_horz.set(maximized);
self.set_net_wm_state(data).await;
}
#[expect(dead_code)]
async fn set_fullscreen(&self, data: &Rc<XwindowData>, fullscreen: bool) {
if false {
// NOTE: We do not want to inform the program if the user changes the fullscreen
// status of the window. Programs usually provide an in-program way to enter/exit
// fullscreen mode.
data.info.fullscreen.set(fullscreen);
self.set_net_wm_state(data).await;
}
}
async fn send_wm_message(&self, window: &Rc<XwindowData>, event_mask: u32, data: &[u32]) {
let event = ClientMessage {
format: 32,
window: window.window_id,
ty: self.atoms.WM_PROTOCOLS,
data,
};
if let Err(e) = self
.c
.send_event(false, window.window_id, event_mask, &event)
.await
{
log::error!("Could not send WM_PROTOCOLS message: {}", ErrorFmt(e));
}
}
async fn focus_window(
&mut self,
window: Option<&Rc<XwindowData>>,
initiator: Initiator,
send_to_x: bool,
) {
// log::info!("xwm focus_window {:?}", window.map(|w| w.window_id));
if let Some(old) = mem::replace(&mut self.focus_window, window.cloned()) {
// log::info!("xwm unfocus {:?}", old.window_id);
self.set_net_wm_state(&old).await;
}
let window = match window {
Some(w) => w,
_ => {
if let Err(e) = self
.c
.call(&SetInputFocus {
revert_to: INPUT_FOCUS_POINTER_ROOT,
focus: 0,
time: 0,
})
.await
{
log::error!("Could not unset pointer focus: {}", ErrorFmt(e));
}
return;
}
};
if window.info.override_redirect.get() {
// log::info!("xwm or => return");
return;
}
if initiator == Initiator::X
&& let Some(window) = window.window.get()
{
let seats = self.state.globals.seats.lock();
for seat in seats.values() {
seat.focus_toplevel(window.clone());
}
}
if send_to_x {
let accepts_input = window.info.icccm_hints.input.get();
let mask = if accepts_input {
EVENT_MASK_SUBSTRUCTURE_REDIRECT
} else {
0
};
self.send_wm_message(window, mask, &[self.atoms.WM_TAKE_FOCUS, 0])
.await;
if accepts_input {
let sif = SetInputFocus {
revert_to: INPUT_FOCUS_POINTER_ROOT,
focus: window.window_id,
time: 0,
};
let (_, serial) = self.c.call_with_serial(&sif);
self.last_input_serial = serial;
}
}
self.set_net_wm_state(window).await;
}
async fn set_net_wm_state(&self, data: &Rc<XwindowData>) {
let mut args = SmallVec::<[_; 6]>::new();
if data.info.modal.get() {
args.push(self.atoms._NET_WM_STATE_MODAL);
}
if data.info.fullscreen.get() {
args.push(self.atoms._NET_WM_STATE_FULLSCREEN);
}
if data.info.maximized_vert.get() {
args.push(self.atoms._NET_WM_STATE_MAXIMIZED_VERT);
}
if data.info.maximized_horz.get() {
args.push(self.atoms._NET_WM_STATE_MAXIMIZED_HORZ);
}
if data.info.minimized.get() {
args.push(self.atoms._NET_WM_STATE_HIDDEN);
}
if Some(data.window_id) == self.focus_window.as_ref().map(|w| w.window_id) {
args.push(self.atoms._NET_WM_STATE_FOCUSED);
}
let cp = ChangeProperty {
mode: PROP_MODE_REPLACE,
window: data.window_id,
property: self.atoms._NET_WM_STATE,
ty: ATOM_ATOM,
format: 32,
data: uapi::as_bytes(&args[..]),
};
if let Err(e) = self.c.call(&cp).await {
log::error!("Could not set _NET_WM_STATE: {}", ErrorFmt(e));
}
}
async fn create_window(&mut self, data: &Rc<XwindowData>, surface: Rc<WlSurface>) {
if data.window.is_some() {
log::error!("The xwindow has already been constructed");
return;
}
let window = match Xwindow::install(data, &surface) {
Ok(w) => w,
Err(e) => {
log::error!(
"Could not attach the xwindow to the surface: {}",
ErrorFmt(e)
);
return;
}
};
self.state.xwayland.windows.set(window.id, window.clone());
data.window.set(Some(window.clone()));
{
self.load_window_wm_class(data).await;
self.load_window_wm_name(data).await;
self.load_window_wm_transient_for(data).await;
self.load_window_wm_protocols(data).await;
self.load_window_wm_hints(data).await;
self.load_window_wm_normal_hints(data).await;
self.load_window_motif_wm_hints(data).await;
self.load_window_net_startup_id(data).await;
self.load_window_net_wm_state(data).await;
self.load_window_net_wm_window_type(data).await;
self.load_window_net_wm_name(data).await;
self.load_window_wm_window_role(data).await;
}
{
let specs = [ResClientIdSpec {
client: data.window_id,
mask: RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID,
}];
let c = ResQueryClientIds {
specs: Cow::Borrowed(&specs),
};
if let Ok(res) = self.c.call(&c).await {
for id in res.get().ids.iter() {
if id.spec.mask.contains(RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID)
&& let Some(first) = id.value.first()
{
data.info.pid.set(Some(*first));
break;
}
}
}
}
window.map_status_changed();
}
async fn handle_xwayland_surface_created(&mut self, surface: WlSurfaceId) {
let surface = match self.client.lookup(surface) {
Ok(s) => s,
_ => return,
};
let data = match self.windows_by_surface_id.get(&surface.id) {
Some(w) => w.clone(),
_ => return,
};
self.create_window(&data, surface).await;
}
async fn handle_xwayland_surface_serial_assigned(&mut self, surface: WlSurfaceId) {
let surface = match self.client.lookup(surface) {
Ok(s) => s,
_ => return,
};
let serial = match surface.xwayland_serial() {
Some(s) => s,
_ => return,
};
let data = match self.windows_by_surface_serial.get(&serial) {
Some(w) => w.clone(),
_ => return,
};
self.create_window(&data, surface).await;
}
fn handle_xwayland_surface_destroyed(&mut self, surface: WlSurfaceId, serial: Option<u64>) {
self.windows_by_surface_id.remove(&surface);
if let Some(serial) = serial {
self.windows_by_surface_serial.remove(&serial);
}
}
async fn handle_event(&mut self, event: &Event) {
let res = match event.ext() {
Some(ex) => self.handle_extension_event(ex, event).await,
_ => self.handle_core_event(event).await,
};
if let Err(e) = res {
log::warn!("Could not handle an event: {}", ErrorFmt(e));
}
}
async fn handle_extension_event(
&mut self,
ex: Extension,
event: &Event,
) -> Result<(), XWaylandError> {
match ex {
Extension::XFIXES => self.handle_xfixes_event(event).await,
_ => Ok(()),
}
}
async fn handle_core_event(&mut self, event: &Event) -> Result<(), XWaylandError> {
match event.code() {
MapRequest::OPCODE => self.handle_map_request(event).await,
MapNotify::OPCODE => self.handle_map_notify(event).await,
ConfigureRequest::OPCODE => self.handle_configure_request(event).await,
ConfigureNotify::OPCODE => self.handle_configure_notify(event),
ClientMessage::OPCODE => self.handle_client_message(event).await,
CreateNotify::OPCODE => self.handle_create_notify(event).await,
DestroyNotify::OPCODE => self.handle_destroy_notify(event).await,
PropertyNotify::OPCODE => self.handle_property_notify(event).await,
FocusIn::OPCODE => self.handle_focus_in(event).await,
UnmapNotify::OPCODE => self.handle_unmap_notify(event).await,
SelectionNotify::OPCODE => self.handle_selection_notify(event).await,
SelectionRequest::OPCODE => self.handle_selection_request(event).await,
_ => Ok(()),
}
}
async fn handle_unmap_notify(&mut self, revent: &Event) -> Result<(), XWaylandError> {
let event: UnmapNotify = revent.parse()?;
let data = match self.windows.get(&event.window) {
Some(w) => w,
_ => return Ok(()),
};
if data.map_link.replace(None).is_some() {
self.num_mapped -= 1;
self.set_net_client_list().await;
}
data.info.mapped.set(false);
if let Some(win) = data.window.get() {
win.map_status_changed();
}
self.set_wm_state(data, ICCCM_WM_STATE_WITHDRAWN).await;
Ok(())
}
async fn handle_focus_in(&mut self, revent: &Event) -> Result<(), XWaylandError> {
let event: FocusIn = revent.parse()?;
// log::info!("xwm focus_in {}", event.event);
if matches!(event.mode, NOTIFY_MODE_GRAB | NOTIFY_MODE_UNGRAB) {
// log::info!("xwm GRAB/UNGRAB");
return Ok(());
}
if matches!(event.detail, NOTIFY_DETAIL_POINTER) {
// log::info!("xwm POINTER");
return Ok(());
}
let new_window = self.windows.get(&event.event);
let mut focus_window = self.focus_window.as_ref();
let mut send_to_x = true;
if let Some(window) = new_window
&& let Some(w) = window.window.get()
&& let Some(prev) = focus_window
{
let prev_pid = prev.info.pid.get();
let new_pid = window.info.pid.get();
if prev_pid.is_some()
&& prev_pid == new_pid
&& revent.serial() >= self.last_input_serial
&& w.x.surface.node_visible()
{
// log::info!("xwm ACCEPT");
focus_window = new_window;
send_to_x = false;
}
}
let fw = focus_window.cloned();
self.focus_window(fw.as_ref(), Initiator::X, send_to_x)
.await;
Ok(())
}
async fn close_window(&mut self, window: &Rc<XwindowData>) {
if window.info.protocols.contains(&self.atoms.WM_DELETE_WINDOW) {
self.send_wm_message(window, 0, &[self.atoms.WM_DELETE_WINDOW])
.await;
} else {
self.c.call(&KillClient {
resource: window.window_id,
});
}
}
async fn activate_window(&mut self, window: Option<&Rc<XwindowData>>, initiator: Initiator) {
// log::info!("xwm activate_window {:?}", window.map(|w| w.window_id));
if self.focus_window.as_ref().map(|w| w.window_id) == window.map(|w| w.window_id) {
return;
}
if let Some(w) = window {
if w.destroyed.get() || w.info.override_redirect.get() {
return;
}
if w.info.minimized.get() {
self.set_minimized(w, false).await;
}
}
self.set_net_active_window(window).await;
self.focus_window(window, initiator, true).await;
if let Some(w) = window {
self.move_to_top_of_stack(w);
self.configure_stack_position(w).await;
}
}
async fn set_net_active_window(&mut self, window: Option<&Rc<XwindowData>>) {
let id = window.map(|w| w.window_id).unwrap_or(0);
let cp = ChangeProperty {
mode: PROP_MODE_REPLACE,
window: self.root,
property: self.atoms._NET_ACTIVE_WINDOW,
ty: self.atoms.WINDOW,
format: 32,
data: uapi::as_bytes(&id),
};
if let Err(e) = self.c.call(&cp).await {
log::error!("Could not set active window: {}", ErrorFmt(e));
}
}
async fn handle_property_notify(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: PropertyNotify = event.parse()?;
// let name = self.c.call(&GetAtomName { atom: event.atom }).await;
// if let Ok(name) = name {
// log::info!("{}", name.get().name);
// }
let data = match self.windows.get(&event.window) {
Some(w) => w,
_ => return Ok(()),
};
if event.atom == ATOM_WM_CLASS {
// log::debug!("ATOM_WM_CLASS changed");
self.load_window_wm_class(data).await;
} else if event.atom == ATOM_WM_NAME {
// log::debug!("ATOM_WM_NAME changed");
self.load_window_wm_name(data).await;
} else if event.atom == ATOM_WM_TRANSIENT_FOR {
// log::debug!("ATOM_WM_TRANSIENT_FOR changed");
self.load_window_wm_transient_for(data).await;
} else if event.atom == self.atoms.WM_PROTOCOLS {
// log::debug!("WM_PROTOCOLS changed");
self.load_window_wm_protocols(data).await;
} else if event.atom == self.atoms.WM_HINTS {
// log::debug!("WM_HINTS changed");
self.load_window_wm_hints(data).await;
} else if event.atom == self.atoms.WM_NORMAL_HINTS {
// log::debug!("WM_NORMAL_HINTS changed");
self.load_window_wm_normal_hints(data).await;
} else if event.atom == self.atoms._MOTIF_WM_HINTS {
// log::debug!("_MOTIF_WM_HINTS changed");
self.load_window_motif_wm_hints(data).await;
} else if event.atom == self.atoms._NET_STARTUP_ID {
// log::debug!("_NET_STARTUP_ID changed");
self.load_window_net_startup_id(data).await;
} else if event.atom == self.atoms._NET_WM_STATE {
// log::debug!("_NET_WM_STATE changed");
self.load_window_net_wm_state(data).await;
} else if event.atom == self.atoms._NET_WM_WINDOW_TYPE {
// log::debug!("_NET_WM_WINDOW_TYPE changed");
self.load_window_net_wm_window_type(data).await;
} else if event.atom == self.atoms._NET_WM_NAME {
// log::debug!("_NET_WM_NAME changed");
self.load_window_net_wm_name(data).await;
} else if event.atom == self.atoms.WM_WINDOW_ROLE {
// log::debug!("WM_WINDOW_ROLE changed");
self.load_window_wm_window_role(data).await;
}
Ok(())
}
async fn handle_destroy_notify(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: DestroyNotify = event.parse()?;
let data = match self.windows.remove(&event.window) {
Some(w) => w,
_ => return Ok(()),
};
// log::info!("xwm destroy_notify {}", event.window);
data.destroyed.set(true);
data.stack_link.borrow_mut().take();
data.map_link.take();
if let Some(sid) = data.surface_id.take() {
self.windows_by_surface_id.remove(&sid);
}
if let Some(serial) = data.surface_serial.take() {
self.windows_by_surface_serial.remove(&serial);
}
if let Some(window) = data.window.take() {
window.queue_spawn_out();
window.destroy();
}
if let Some(parent) = data.parent.take() {
parent.children.remove(&data.window_id);
}
{
let mut children = data.children.lock();
for child in children.drain_values() {
child.parent.set(None);
}
}
if self.focus_window.as_ref().map(|w| w.window_id) == Some(event.window) {
self.activate_window(None, Initiator::X).await;
}
Ok(())
}
async fn handle_create_notify(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: CreateNotify = event.parse()?;
if event.window == self.xwin {
return Ok(());
}
let data = Rc::new(XwindowData::new(&self.state, &event, &self.client));
let cwa = ChangeWindowAttributes {
window: event.window,
values: CreateWindowValues {
event_mask: Some(EVENT_MASK_PROPERTY_CHANGE | EVENT_MASK_FOCUS_CHANGE),
..Default::default()
},
};
if let Err(e) = self.c.call(&cwa).await {
log::error!(
"Could not subscribe to events of new window: {}",
ErrorFmt(e)
);
}
if let Ok(res) = self
.c
.call(&GetGeometry {
drawable: event.window,
})
.await
{
data.info.has_alpha.set(res.get().depth == 32);
}
self.windows.insert(event.window, data);
Ok(())
}
async fn handle_client_message(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: ClientMessage = event.parse()?;
if event.ty == self.atoms.WL_SURFACE_ID {
self.handle_wl_surface_id(&event).await?;
} else if event.ty == self.atoms._NET_WM_STATE {
self.handle_net_wm_state(&event).await?;
} else if event.ty == self.atoms._NET_ACTIVE_WINDOW {
self.handle_net_active_window(&event).await?;
} else if event.ty == self.atoms._NET_STARTUP_INFO
|| event.ty == self.atoms._NET_STARTUP_INFO_BEGIN
{
self.handle_net_startup_info(&event).await?;
} else if event.ty == self.atoms.WM_CHANGE_STATE {
self.handle_wm_change_state(&event).await?;
} else if event.ty == self.atoms._NET_WM_MOVERESIZE {
self.handle_net_wm_moveresize(&event).await?;
} else if event.ty == self.atoms.WL_SURFACE_SERIAL {
self.handle_wl_surface_serial(&event).await?;
}
Ok(())
}
async fn set_net_client_list_stacking(&mut self) {
let mut windows = Vec::with_capacity(self.num_stacked);
for w in self.stack_list.iter() {
windows.push(w.window_id);
}
let cp = ChangeProperty {
mode: PROP_MODE_REPLACE,
window: self.root,
property: self.atoms._NET_CLIENT_LIST_STACKING,
ty: ATOM_WINDOW,
format: 32,
data: uapi::as_bytes(&windows[..]),
};
if let Err(e) = self.c.call(&cp).await {
log::error!("Could not set _NET_CLIENT_LIST_STACKING: {}", ErrorFmt(e));
}
}
async fn set_net_client_list(&self) {
let mut windows = Vec::with_capacity(self.num_mapped);
for w in self.map_list.iter() {
windows.push(w.window_id);
}
let cp = ChangeProperty {
mode: PROP_MODE_REPLACE,
window: self.root,
property: self.atoms._NET_CLIENT_LIST,
ty: ATOM_WINDOW,
format: 32,
data: uapi::as_bytes(&windows[..]),
};
if let Err(e) = self.c.call(&cp).await {
log::error!("Could not set _NET_CLIENT_LIST: {}", ErrorFmt(e));
}
}
fn update_override_redirect(&self, data: &Rc<XwindowData>, or: u8) {
let or = or != 0;
if data.info.override_redirect.replace(or) != or
&& let Some(window) = data.window.get()
{
window.tl_destroy();
window.update_toplevel();
window.map_status_changed();
}
}
async fn handle_map_notify(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: MapNotify = event.parse()?;
let data = match self.windows.get(&event.window) {
Some(d) => d.clone(),
_ => return Ok(()),
};
self.update_override_redirect(&data, event.override_redirect);
data.info.mapped.set(true);
if let Some(win) = data.window.get() {
win.map_status_changed();
}
self.configure_stack_position(&data).await;
Ok(())
}
async fn handle_map_request(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: MapRequest = event.parse()?;
let data = match self.windows.get(&event.window) {
Some(w) => w.clone(),
_ => return Ok(()),
};
self.set_wm_state(&data, ICCCM_WM_STATE_NORMAL).await;
self.set_net_wm_state(&data).await;
if data
.map_link
.replace(Some(self.map_list.add_last(data.clone())))
.is_none()
{
self.num_mapped += 1;
}
self.set_net_client_list().await;
let pending = data.info.pending_extents.get();
if pending.width() > 0 && pending.height() > 0 {
let dummy = Rect::new_sized_saturating(0, 0, 1, 1);
for rect in [dummy, pending] {
let mut x = rect.x1();
let mut y = rect.y1();
let mut width = rect.width();
let mut height = rect.height();
logical_to_client_wire_scale!(self.client, x, y, width, height);
let cw = ConfigureWindow {
window: data.window_id,
values: ConfigureWindowValues {
x: Some(x),
y: Some(y),
width: Some(width as _),
height: Some(height as _),
..Default::default()
},
};
let _ = self.c.call(&cw).await;
}
}
self.move_to_top_of_stack(&data);
let mw = MapWindow {
window: event.window,
};
if let Err(e) = self.c.call(&mw).await {
log::error!("Could not map window: {}", ErrorFmt(e));
}
Ok(())
}
fn move_to_top_of_stack(&mut self, window: &Rc<XwindowData>) {
let link = self.stack_list.add_last(window.clone());
*window.stack_link.borrow_mut() = Some(link);
}
async fn configure_stack_position(&mut self, window: &Rc<XwindowData>) {
let sl = window.stack_link.borrow_mut();
let sl = match sl.deref() {
Some(sl) => sl,
_ => return,
};
let (sibling, stack_mode) = match sl.prev() {
Some(n) => (Some(n), STACK_MODE_ABOVE),
_ => match sl.next() {
Some(n) => (Some(n), STACK_MODE_BELOW),
_ => (None, STACK_MODE_ABOVE),
},
};
let res = self
.c
.call(&ConfigureWindow {
window: window.window_id,
values: ConfigureWindowValues {
sibling: sibling.map(|s| s.window_id),
stack_mode: Some(stack_mode),
..Default::default()
},
})
.await;
if let Err(e) = res {
log::warn!("Could not restack window: {}", ErrorFmt(e));
}
self.set_net_client_list_stacking().await;
}
async fn set_wm_state(&self, data: &Rc<XwindowData>, state: u32) {
let property = [state, 0];
let cp = ChangeProperty {
mode: PROP_MODE_REPLACE,
window: data.window_id,
property: self.atoms.WM_STATE,
ty: self.atoms.WM_STATE,
format: 32,
data: uapi::as_bytes(&property[..]),
};
self.c.call(&cp);
}
fn handle_configure_notify(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: ConfigureNotify = event.parse()?;
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
self.update_override_redirect(data, event.override_redirect);
if data.info.override_redirect.get() {
let mut x = event.x as i32;
let mut y = event.y as i32;
let mut width = event.width as i32;
let mut height = event.height as i32;
client_wire_scale_to_logical!(self.client, x, y, width, height);
let extents = Rect::new_sized_saturating(x, y, width, height);
if let Some(window) = data.window.get() {
window.tl_change_extents(&extents);
self.state.tree_changed();
} else {
data.info.pending_extents.set(extents);
}
}
Ok(())
}
async fn handle_configure_request(&mut self, event: &Event) -> Result<(), XWaylandError> {
let event: ConfigureRequest = event.parse()?;
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
if let Some(window) = data.window.get()
&& window.is_mapped()
{
return Ok(());
}
let de = data.info.pending_extents.get();
let mut x1 = de.x1();
let mut y1 = de.y1();
let mut width = de.width();
let mut height = de.height();
if event.value_mask.contains(CONFIG_WINDOW_X) {
x1 = event.x as _;
client_wire_scale_to_logical!(self.client, x1);
}
if event.value_mask.contains(CONFIG_WINDOW_Y) {
y1 = event.y as _;
client_wire_scale_to_logical!(self.client, y1);
}
if event.value_mask.contains(CONFIG_WINDOW_WIDTH) {
width = event.width as _;
client_wire_scale_to_logical!(self.client, width);
}
if event.value_mask.contains(CONFIG_WINDOW_HEIGHT) {
height = event.height as _;
client_wire_scale_to_logical!(self.client, height);
}
data.info
.pending_extents
.set(Rect::new_sized_saturating(x1, y1, width, height));
Ok(())
}
async fn handle_net_wm_moveresize(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let _data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
let _detail = event.data[2];
Ok(())
}
async fn handle_wm_change_state(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
let minimize = match event.data[0] {
ICCCM_WM_STATE_NORMAL => false,
ICCCM_WM_STATE_ICONIC => self.handle_minimize_requested(data).await,
_ => return Ok(()),
};
data.info.minimized.set(minimize);
self.set_net_wm_state(data).await;
Ok(())
}
async fn handle_minimize_requested(&self, data: &Rc<XwindowData>) -> bool {
if let Some(w) = data.window.get()
&& w.toplevel_data.active_surfaces.active()
{
self.set_wm_state(data, ICCCM_WM_STATE_NORMAL).await;
return false;
}
self.set_wm_state(data, ICCCM_WM_STATE_ICONIC).await;
true
}
async fn handle_net_startup_info(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
let mut startup_info = data.startup_info.borrow_mut();
let mut msg = uapi::as_bytes(event.data);
let mut end = false;
if let Some(pos) = msg.find_byte(0) {
end = true;
msg = &msg[..pos];
}
startup_info.extend_from_slice(msg);
if !end {
return Ok(());
}
if let Some(id) = startup_info.strip_prefix(b"remove: ID=") {
log::info!("Got startup id {}", id.as_bstr());
} else {
log::warn!("Unhandled startup info: {}", startup_info.as_bstr());
}
mem::take(startup_info.deref_mut());
Ok(())
}
async fn handle_net_active_window(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
let fw = match &self.focus_window {
Some(w) => w,
_ => return Ok(()),
};
if data.info.pid.is_none() || data.info.pid.get() != fw.info.pid.get() {
return Ok(());
}
let win = match data.window.get() {
Some(w) => w,
_ => return Ok(()),
};
if win.toplevel_data.visible.get() {
let seats = self.state.globals.seats.lock();
for (_, seat) in seats.deref() {
seat.focus_toplevel(win.clone());
}
} else {
win.x.surface.request_activation();
}
Ok(())
}
async fn handle_net_wm_state(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let data = match self.windows.get(&event.window) {
Some(d) => d,
_ => return Ok(()),
};
let mut changed = false;
let mut fullscreen = data.info.fullscreen.get();
let mut maximized_horz = data.info.maximized_horz.get();
let mut maximized_vert = data.info.maximized_vert.get();
let mut minimized = data.info.minimized.get();
let mut modal = data.info.modal.get();
let action = event.data[0];
let mut update = |prop: &mut bool| {
let new = match action {
_NET_WM_STATE_REMOVE => false,
_NET_WM_STATE_ADD => true,
_NET_WM_STATE_TOGGLE => !*prop,
_ => return,
};
if mem::replace(prop, new) != new {
changed = true;
}
};
for p in [event.data[1], event.data[2]] {
if p == self.atoms._NET_WM_STATE_MODAL {
update(&mut modal);
} else if p == self.atoms._NET_WM_STATE_FULLSCREEN {
update(&mut fullscreen);
} else if p == self.atoms._NET_WM_STATE_MAXIMIZED_VERT {
update(&mut maximized_vert);
} else if p == self.atoms._NET_WM_STATE_MAXIMIZED_HORZ {
update(&mut maximized_horz);
} else if p == self.atoms._NET_WM_STATE_HIDDEN {
update(&mut minimized);
}
}
if !changed {
return Ok(());
}
if minimized != data.info.minimized.get() {
if minimized {
minimized = self.handle_minimize_requested(data).await;
}
}
if fullscreen != data.info.fullscreen.get()
&& let Some(w) = data.window.get()
{
w.tl_set_fullscreen(fullscreen, None);
}
data.info.fullscreen.set(fullscreen);
data.info.maximized_horz.set(maximized_horz);
data.info.maximized_vert.set(maximized_vert);
data.info.minimized.set(minimized);
data.info.modal.set(modal);
self.update_wants_floating(data);
self.set_net_wm_state(data).await;
Ok(())
}
async fn handle_wl_surface_serial(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let serial = event.data[0] as u64 | ((event.data[1] as u64) << 32);
if serial <= self.last_surface_serial {
log::error!(
"Surface serial is not monotonic: {} <= {}",
serial,
self.last_surface_serial
);
return Ok(());
}
self.last_surface_serial = serial;
let data = match self.windows.get(&event.window) {
Some(d) => d.clone(),
_ => return Ok(()),
};
if let Some(old) = data.surface_serial.replace(Some(serial)) {
self.windows_by_surface_serial.remove(&old);
}
if let Some(old) = data.window.take() {
old.break_loops();
}
self.windows_by_surface_serial.insert(serial, data.clone());
if let Some(surface) = self.client.surfaces_by_xwayland_serial.get(&serial) {
self.create_window(&data, surface).await;
}
Ok(())
}
async fn handle_wl_surface_id(
&mut self,
event: &ClientMessage<'_>,
) -> Result<(), XWaylandError> {
let data = match self.windows.get(&event.window) {
Some(d) => d.clone(),
_ => return Ok(()),
};
if data.surface_id.is_some() {
log::error!("Surface id is already set");
return Ok(());
}
let surface_id = event.data[0];
let surface_id = WlSurfaceId::from_raw(surface_id);
data.surface_id.set(Some(surface_id));
self.windows_by_surface_id.insert(surface_id, data.clone());
if let Ok(surface) = self.client.lookup(surface_id) {
self.create_window(&data, surface).await;
}
Ok(())
}
fn update_wants_floating(&self, data: &Rc<XwindowData>) {
let res = data.info.modal.get()
|| data
.info
.window_types
.contains(&self.atoms._NET_WM_WINDOW_TYPE_DIALOG)
|| data
.info
.window_types
.contains(&self.atoms._NET_WM_WINDOW_TYPE_UTILITY)
|| data
.info
.window_types
.contains(&self.atoms._NET_WM_WINDOW_TYPE_TOOLBAR)
|| data
.info
.window_types
.contains(&self.atoms._NET_WM_WINDOW_TYPE_SPLASH)
|| {
let max_w = data.info.normal_hints.max_width.get();
let min_w = data.info.normal_hints.min_width.get();
let max_h = data.info.normal_hints.max_height.get();
let min_h = data.info.normal_hints.min_height.get();
max_w > 0 && max_h > 0 && max_w == min_w && max_h == min_h
};
data.info.wants_floating.set(res);
}
}