mirror of
https://github.com/wez/wezterm.git
synced 2024-12-29 00:21:57 +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
|
#### Changed
|
||||||
* Removed Last Resort fallback font
|
* 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
|
#### Updated
|
||||||
* Bundled Nerd Font Symbols font to v2.2.2
|
* Bundled Nerd Font Symbols font to v2.2.2
|
||||||
|
@ -11,7 +11,7 @@ use mio::event::Source;
|
|||||||
use mio::unix::SourceFd;
|
use mio::unix::SourceFd;
|
||||||
use mio::{Events, Interest, Poll, Registry, Token};
|
use mio::{Events, Interest, Poll, Registry, Token};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@ -48,6 +48,8 @@ pub struct XConnection {
|
|||||||
pub atom_net_wm_name: Atom,
|
pub atom_net_wm_name: Atom,
|
||||||
pub atom_net_wm_icon: Atom,
|
pub atom_net_wm_icon: Atom,
|
||||||
pub atom_net_move_resize_window: 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) xrm: RefCell<HashMap<String, String>>,
|
||||||
pub(crate) windows: RefCell<HashMap<xcb::x::Window, Arc<Mutex<XWindowInner>>>>,
|
pub(crate) windows: RefCell<HashMap<xcb::x::Window, Arc<Mutex<XWindowInner>>>>,
|
||||||
should_terminate: RefCell<bool>,
|
should_terminate: RefCell<bool>,
|
||||||
@ -58,6 +60,7 @@ pub struct XConnection {
|
|||||||
pub(crate) ime_process_event_result: RefCell<anyhow::Result<()>>,
|
pub(crate) ime_process_event_result: RefCell<anyhow::Result<()>>,
|
||||||
pub(crate) has_randr: bool,
|
pub(crate) has_randr: bool,
|
||||||
pub(crate) atom_names: RefCell<HashMap<Atom, String>>,
|
pub(crate) atom_names: RefCell<HashMap<Atom, String>>,
|
||||||
|
pub(crate) supported: RefCell<HashSet<Atom>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for XConnection {
|
impl std::ops::Deref for XConnection {
|
||||||
@ -345,6 +348,21 @@ impl XConnection {
|
|||||||
|
|
||||||
let dpi = compute_default_dpi(&self.xrm.borrow(), &self.xsettings.borrow());
|
let dpi = compute_default_dpi(&self.xrm.borrow(), &self.xsettings.borrow());
|
||||||
*self.default_dpi.borrow_mut() = dpi;
|
*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) {
|
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_name = Self::intern_atom(&conn, "_NET_WM_NAME")?;
|
||||||
let atom_net_wm_icon = Self::intern_atom(&conn, "_NET_WM_ICON")?;
|
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_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);
|
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_pid,
|
||||||
atom_net_wm_name,
|
atom_net_wm_name,
|
||||||
atom_net_move_resize_window,
|
atom_net_move_resize_window,
|
||||||
|
atom_net_wm_moveresize,
|
||||||
|
atom_net_supported,
|
||||||
atom_net_wm_icon,
|
atom_net_wm_icon,
|
||||||
keyboard,
|
keyboard,
|
||||||
kbd_ev,
|
kbd_ev,
|
||||||
@ -629,6 +651,7 @@ impl XConnection {
|
|||||||
ime_process_event_result: RefCell::new(Ok(())),
|
ime_process_event_result: RefCell::new(Ok(())),
|
||||||
has_randr,
|
has_randr,
|
||||||
atom_names: RefCell::new(HashMap::new()),
|
atom_names: RefCell::new(HashMap::new()),
|
||||||
|
supported: RefCell::new(HashSet::new()),
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -684,6 +707,8 @@ impl XConnection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.update_net_supported();
|
||||||
|
|
||||||
Ok(conn)
|
Ok(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,15 @@ pub(crate) struct XWindowInner {
|
|||||||
paint_throttled: bool,
|
paint_throttled: bool,
|
||||||
pending: Vec<WindowEvent>,
|
pending: Vec<WindowEvent>,
|
||||||
sure_about_geometry: bool,
|
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 {
|
impl Drop for XWindowInner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.window_id != xcb::x::Window::none() {
|
if self.window_id != xcb::x::Window::none() {
|
||||||
@ -155,7 +162,28 @@ impl XWindowInner {
|
|||||||
self.queue_pending(WindowEvent::NeedRepaint);
|
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<()> {
|
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));
|
self.events.dispatch(WindowEvent::MouseEvent(event));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -326,6 +354,11 @@ impl XWindowInner {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.copy_and_paste.time = time;
|
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 {
|
let kind = match detail {
|
||||||
b @ 1..=3 => {
|
b @ 1..=3 => {
|
||||||
let button = match b {
|
let button = match b {
|
||||||
@ -1066,6 +1099,9 @@ impl XWindow {
|
|||||||
invalidated: false,
|
invalidated: false,
|
||||||
pending: vec![],
|
pending: vec![],
|
||||||
sure_about_geometry: false,
|
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);
|
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 ask the window manager to move the window for us so that
|
||||||
// we don't have to deal with adjusting for the frame size.
|
// we don't have to deal with adjusting for the frame size.
|
||||||
// Note that neither this technique or the configure_window
|
// 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) {
|
fn set_window_position(&self, coords: ScreenPoint) {
|
||||||
XConnection::with_window_inner(self.0, move |inner| {
|
XConnection::with_window_inner(self.0, move |inner| {
|
||||||
inner.set_window_position(coords);
|
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) {
|
fn set_window_position(&self, coords: ScreenPoint) {
|
||||||
match self {
|
match self {
|
||||||
Self::X11(x) => x.set_window_position(coords),
|
Self::X11(x) => x.set_window_position(coords),
|
||||||
|
Loading…
Reference in New Issue
Block a user