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:
parent
5a754e44e7
commit
006e19813f
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user