1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-27 15:37:29 +03:00

x11: use _NET_WM_MOVERESIZE to drag by tab bar

refs: https://github.com/wez/wezterm/issues/2530
This commit is contained in:
Wez Furlong 2022-09-18 17:20:03 -07:00
parent 5a754e44e7
commit 006e19813f
4 changed files with 152 additions and 2 deletions

View File

@ -50,6 +50,8 @@ As features stabilize some brief notes about them will accumulate here.
#### Changed
* Removed Last Resort fallback font
* X11: use `_NET_WM_MOVERESIZE` to drag by tab bar, when supported by the WM
[#2530](https://github.com/wez/wezterm/issues/2530)
#### Updated
* Bundled Nerd Font Symbols font to v2.2.2

View File

@ -11,7 +11,7 @@ use mio::event::Source;
use mio::unix::SourceFd;
use mio::{Events, Interest, Poll, Registry, Token};
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
@ -48,6 +48,8 @@ pub struct XConnection {
pub atom_net_wm_name: Atom,
pub atom_net_wm_icon: Atom,
pub atom_net_move_resize_window: Atom,
pub atom_net_wm_moveresize: Atom,
pub atom_net_supported: Atom,
pub(crate) xrm: RefCell<HashMap<String, String>>,
pub(crate) windows: RefCell<HashMap<xcb::x::Window, Arc<Mutex<XWindowInner>>>>,
should_terminate: RefCell<bool>,
@ -58,6 +60,7 @@ pub struct XConnection {
pub(crate) ime_process_event_result: RefCell<anyhow::Result<()>>,
pub(crate) has_randr: bool,
pub(crate) atom_names: RefCell<HashMap<Atom, String>>,
pub(crate) supported: RefCell<HashSet<Atom>>,
}
impl std::ops::Deref for XConnection {
@ -345,6 +348,21 @@ impl XConnection {
let dpi = compute_default_dpi(&self.xrm.borrow(), &self.xsettings.borrow());
*self.default_dpi.borrow_mut() = dpi;
self.update_net_supported();
}
fn update_net_supported(&self) {
if let Ok(reply) = self.send_and_wait_request(&xcb::x::GetProperty {
delete: false,
window: self.root,
property: self.atom_net_supported,
r#type: xcb::x::ATOM_ATOM,
long_offset: 0,
long_length: 1024,
}) {
let supported: HashSet<Atom> = reply.value::<Atom>().iter().copied().collect();
*self.supported.borrow_mut() = supported;
}
}
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
@ -509,6 +527,8 @@ impl XConnection {
let atom_net_wm_name = Self::intern_atom(&conn, "_NET_WM_NAME")?;
let atom_net_wm_icon = Self::intern_atom(&conn, "_NET_WM_ICON")?;
let atom_net_move_resize_window = Self::intern_atom(&conn, "_NET_MOVERESIZE_WINDOW")?;
let atom_net_wm_moveresize = Self::intern_atom(&conn, "_NET_WM_MOVERESIZE")?;
let atom_net_supported = Self::intern_atom(&conn, "_NET_SUPPORTED")?;
let has_randr = conn.active_extensions().any(|e| e == xcb::Extension::RandR);
@ -614,6 +634,8 @@ impl XConnection {
atom_net_wm_pid,
atom_net_wm_name,
atom_net_move_resize_window,
atom_net_wm_moveresize,
atom_net_supported,
atom_net_wm_icon,
keyboard,
kbd_ev,
@ -629,6 +651,7 @@ impl XConnection {
ime_process_event_result: RefCell::new(Ok(())),
has_randr,
atom_names: RefCell::new(HashMap::new()),
supported: RefCell::new(HashSet::new()),
});
{
@ -684,6 +707,8 @@ impl XConnection {
});
}
conn.update_net_supported();
Ok(conn)
}

View File

@ -74,8 +74,15 @@ pub(crate) struct XWindowInner {
paint_throttled: bool,
pending: Vec<WindowEvent>,
sure_about_geometry: bool,
current_mouse_event: Option<MouseEvent>,
window_drag_position: Option<ScreenPoint>,
dragging: bool,
}
/// <https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm46409506331616>
const _NET_WM_MOVERESIZE_MOVE: u32 = 8;
const _NET_WM_MOVERESIZE_CANCEL: u32 = 11;
impl Drop for XWindowInner {
fn drop(&mut self) {
if self.window_id != xcb::x::Window::none() {
@ -155,7 +162,28 @@ impl XWindowInner {
self.queue_pending(WindowEvent::NeedRepaint);
}
fn cancel_drag(&mut self) -> bool {
if self.dragging {
log::debug!("cancel_drag");
self.net_wm_moveresize(0, 0, _NET_WM_MOVERESIZE_CANCEL, 0);
self.dragging = false;
if let Some(event) = self.current_mouse_event.take() {
self.do_mouse_event(MouseEvent {
kind: MouseEventKind::Release(MousePress::Left),
..event
})
.ok();
}
return true;
}
false
}
fn do_mouse_event(&mut self, event: MouseEvent) -> anyhow::Result<()> {
if self.cancel_drag() {
return Ok(());
}
self.current_mouse_event.replace(event.clone());
self.events.dispatch(WindowEvent::MouseEvent(event));
Ok(())
}
@ -326,6 +354,11 @@ impl XWindowInner {
) -> anyhow::Result<()> {
self.copy_and_paste.time = time;
if self.cancel_drag() {
log::debug!("cancel drag due to button {detail} {state:?}");
return Ok(());
}
let kind = match detail {
b @ 1..=3 => {
let button = match b {
@ -1066,6 +1099,9 @@ impl XWindow {
invalidated: false,
pending: vec![],
sure_about_geometry: false,
current_mouse_event: None,
window_drag_position: None,
dragging: false,
}))
};
@ -1219,7 +1255,72 @@ impl XWindowInner {
let _ = self.adjust_decorations(config.window_decorations);
}
fn set_window_position(&self, coords: ScreenPoint) {
fn net_wm_moveresize(&mut self, x_root: u32, y_root: u32, direction: u32, button: u32) {
let source_indication = 1;
let conn = self.conn();
if !conn
.supported
.borrow()
.contains(&conn.atom_net_wm_moveresize)
{
log::debug!("WM doesn't support _NET_WM_MOVERESIZE");
return;
}
log::debug!("net_wm_moveresize {x_root},{y_root} direction={direction} button={button}");
if direction != _NET_WM_MOVERESIZE_CANCEL {
// Tell the server to ungrab. Even though we haven't explicitly
// grabbed it in our application code, there's an implicit grab
// as part of a mouse drag and the moveresize will do nothing
// if we don't ungrab it.
conn.send_request_no_reply_log(&xcb::x::UngrabPointer {
time: self.copy_and_paste.time,
});
// Flag to ourselves that we are dragging.
// This is also used to gate the fallback of calling
// set_window_position in case the WM doesn't support
// _NET_WM_MOVERESIZE and we returned early above.
self.dragging = true;
}
conn.send_request_no_reply_log(&xcb::x::SendEvent {
propagate: true,
destination: xcb::x::SendEventDest::Window(conn.root),
event_mask: xcb::x::EventMask::SUBSTRUCTURE_REDIRECT
| xcb::x::EventMask::SUBSTRUCTURE_NOTIFY,
event: &xcb::x::ClientMessageEvent::new(
self.window_id,
conn.atom_net_wm_moveresize,
xcb::x::ClientMessageData::Data32([
x_root,
y_root,
direction,
button,
source_indication,
]),
),
});
conn.flush().context("flush moveresize").ok();
}
fn request_drag_move(&mut self) -> anyhow::Result<()> {
let pos = self.window_drag_position.unwrap_or_default();
let x_root = pos.x as u32;
let y_root = pos.y as u32;
let button = 1; // Left
self.net_wm_moveresize(x_root, y_root, _NET_WM_MOVERESIZE_MOVE, button);
Ok(())
}
fn set_window_position(&mut self, coords: ScreenPoint) {
if self.dragging {
return;
}
// We ask the window manager to move the window for us so that
// we don't have to deal with adjusting for the frame size.
// Note that neither this technique or the configure_window
@ -1493,6 +1594,20 @@ impl WindowOps for XWindow {
});
}
fn request_drag_move(&self) {
XConnection::with_window_inner(self.0, move |inner| {
inner.request_drag_move()?;
Ok(())
});
}
fn set_window_drag_position(&self, coords: ScreenPoint) {
XConnection::with_window_inner(self.0, move |inner| {
inner.window_drag_position.replace(coords);
Ok(())
});
}
fn set_window_position(&self, coords: ScreenPoint) {
XConnection::with_window_inner(self.0, move |inner| {
inner.set_window_position(coords);

View File

@ -342,6 +342,14 @@ impl WindowOps for Window {
}
}
fn set_window_drag_position(&self, coords: ScreenPoint) {
match self {
Self::X11(x) => x.set_window_drag_position(coords),
#[cfg(feature = "wayland")]
Self::Wayland(w) => w.set_window_drag_position(coords),
}
}
fn set_window_position(&self, coords: ScreenPoint) {
match self {
Self::X11(x) => x.set_window_position(coords),