1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-21 03:39:16 +03:00

allow dragging by tab bar on linux

This works with X11 on fedora, but the window movement is ignored
by the xwayland machinery on chromeos.
This commit is contained in:
Wez Furlong 2019-11-23 11:46:03 -08:00
parent 01eaa7db08
commit 349a24ccd9
5 changed files with 152 additions and 5 deletions

View File

@ -37,6 +37,7 @@ pub struct TermWindow {
show_tab_bar: bool,
tab_bar: TabBarState,
last_mouse_coords: (usize, i64),
drag_start_coords: Option<Point>,
}
struct Host<'a> {
@ -117,6 +118,19 @@ impl WindowCallbacks for TermWindow {
None => return,
};
match event.kind {
WMEK::Release(MousePress::Left) => self.drag_start_coords = None,
WMEK::Move => {
if let Some(drag) = self.drag_start_coords.as_ref() {
context.set_window_position(ScreenPoint::new(
event.screen_coords.x - drag.x,
event.screen_coords.y - drag.y,
));
}
}
_ => {}
}
let x = (event.coords.x.max(0) / self.render_metrics.cell_size.width) as usize;
let y = (event.coords.y.max(0) / self.render_metrics.cell_size.height) as i64;
@ -124,6 +138,11 @@ impl WindowCallbacks for TermWindow {
self.last_mouse_coords = (x, y);
if self.show_tab_bar && y == 0 {
match event.kind {
WMEK::Press(MousePress::Left) => self.drag_start_coords = Some(event.coords),
_ => {}
}
if let Some(tab_idx) = self.tab_bar.hit_test(x) {
match event.kind {
WMEK::Press(MousePress::Left) => {
@ -387,6 +406,7 @@ impl TermWindow {
show_tab_bar: config.enable_tab_bar,
tab_bar: TabBarState::default(),
last_mouse_coords: (0, -1),
drag_start_coords: None,
}),
)?;

View File

@ -127,7 +127,10 @@ pub enum MouseEventKind {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MouseEvent {
pub kind: MouseEventKind,
/// Coordinates of the mouse relative to the top left of the window
pub coords: crate::Point,
/// The mouse position in screen coordinates
pub screen_coords: crate::ScreenPoint,
pub mouse_buttons: MouseButtons,
pub modifiers: Modifiers,
}

View File

@ -59,6 +59,9 @@ pub type Point = euclid::Point2D<isize, PixelUnit>;
pub type Rect = euclid::Rect<isize, PixelUnit>;
pub type Size = euclid::Size2D<isize, PixelUnit>;
pub struct ScreenPixelUnit;
pub type ScreenPoint = euclid::Point2D<isize, ScreenPixelUnit>;
pub trait PaintContext {
fn get_dimensions(&self) -> Dimensions;
@ -173,6 +176,13 @@ pub trait WindowOps {
/// Resize the inner or client area of the window
fn set_inner_size(&self, width: usize, height: usize) -> Future<()>;
/// Changes the location of the window on the screen.
/// The coordinates are of the top left pixel of the
/// client area.
fn set_window_position(&self, _coords: ScreenPoint) -> Future<()> {
Future::ok(())
}
/// inform the windowing system of the current textual
/// cursor input location. This is used primarily for
/// the platform specific input method editor
@ -238,4 +248,9 @@ pub trait WindowOpsMut {
/// cursor input location. This is used primarily for
/// the platform specific input method editor
fn set_text_cursor_position(&mut self, _cursor: Rect) {}
/// Changes the location of the window on the screen.
/// The coordinates are of the top left pixel of the
/// client area.
fn set_window_position(&self, _coords: ScreenPoint) {}
}

View File

@ -81,8 +81,8 @@ impl TimerList {
pub struct Connection {
pub display: *mut x11::xlib::Display,
conn: xcb::Connection,
screen_num: i32,
conn: xcb_util::ewmh::Connection,
pub screen_num: i32,
pub keyboard: Keyboard,
pub kbd_ev: u8,
pub atom_protocols: xcb::Atom,
@ -370,6 +370,8 @@ impl Connection {
}
let screen_num = unsafe { x11::xlib::XDefaultScreen(display) };
let conn = unsafe { xcb::Connection::from_raw_conn(XGetXCBConnection(display)) };
let conn = xcb_util::ewmh::Connection::connect(conn)
.map_err(|_| failure::err_msg("failed to init ewmh"))?;
unsafe { XSetEventQueueOwner(display, 1) };
let atom_protocols = xcb::intern_atom(&conn, false, "WM_PROTOCOLS")
@ -391,7 +393,7 @@ impl Connection {
.get_reply()?
.atom();
let keysyms = unsafe { xcb_key_symbols_alloc(conn.get_raw_conn()) };
let keysyms = unsafe { xcb_key_symbols_alloc((*conn).get_raw_conn()) };
let shm_available = server_supports_shm();
eprintln!("shm_available: {}", shm_available);
@ -452,10 +454,14 @@ impl Connection {
Ok(conn)
}
pub fn conn(&self) -> &xcb::Connection {
pub fn ewmh_conn(&self) -> &xcb_util::ewmh::Connection {
&self.conn
}
pub fn conn(&self) -> &xcb::Connection {
&*self.conn
}
pub fn screen_num(&self) -> i32 {
self.screen_num
}

View File

@ -3,7 +3,8 @@ use crate::bitmaps::*;
use crate::connection::ConnectionOps;
use crate::{
Color, Dimensions, KeyEvent, MouseButtons, MouseCursor, MouseEvent, MouseEventKind, MousePress,
Operator, PaintContext, Point, Rect, Size, WindowCallbacks, WindowOps, WindowOpsMut,
Operator, PaintContext, Point, Rect, ScreenPoint, Size, WindowCallbacks, WindowOps,
WindowOpsMut,
};
use failure::Fallible;
use promise::Future;
@ -281,6 +282,10 @@ impl WindowInner {
motion.event_x().try_into().unwrap(),
motion.event_y().try_into().unwrap(),
),
screen_coords: ScreenPoint::new(
motion.root_x().try_into().unwrap(),
motion.root_y().try_into().unwrap(),
),
modifiers: xkeysyms::modifiers_from_state(motion.state()),
mouse_buttons: MouseButtons::default(),
};
@ -321,6 +326,10 @@ impl WindowInner {
button_press.event_x().try_into().unwrap(),
button_press.event_y().try_into().unwrap(),
),
screen_coords: ScreenPoint::new(
button_press.root_x().try_into().unwrap(),
button_press.root_y().try_into().unwrap(),
),
modifiers: xkeysyms::modifiers_from_state(button_press.state()),
mouse_buttons: MouseButtons::default(),
};
@ -343,6 +352,54 @@ impl WindowInner {
Ok(())
}
#[allow(dead_code)]
fn disable_decorations(&mut self) -> Fallible<()> {
// Set the motif hints to disable decorations.
// See https://stackoverflow.com/a/1909708
#[repr(C)]
struct MwmHints {
flags: u32,
functions: u32,
decorations: u32,
input_mode: i32,
status: u32,
}
const HINTS_FUNCTIONS: u32 = 1 << 0;
const HINTS_DECORATIONS: u32 = 1 << 1;
const FUNC_ALL: u32 = 1 << 0;
const FUNC_RESIZE: u32 = 1 << 1;
const FUNC_MOVE: u32 = 1 << 2;
const FUNC_MINIMIZE: u32 = 1 << 3;
const FUNC_MAXIMIZE: u32 = 1 << 4;
const FUNC_CLOSE: u32 = 1 << 5;
let hints = MwmHints {
flags: HINTS_DECORATIONS,
functions: 0,
decorations: 0, // off
input_mode: 0,
status: 0,
};
let hints_slice =
unsafe { std::slice::from_raw_parts(&hints as *const _ as *const u32, 5) };
let atom = xcb::intern_atom(self.conn.conn(), false, "_MOTIF_WM_HINTS")
.get_reply()?
.atom();
xcb::change_property(
self.conn.conn(),
xcb::PROP_MODE_REPLACE as u8,
self.window_id,
atom,
atom,
32,
hints_slice,
);
Ok(())
}
}
/// A Window!
@ -438,6 +495,8 @@ impl Window {
&[conn.atom_delete],
);
// window.lock().unwrap().disable_decorations()?;
let window_handle = Window::from_id(window_id);
window.lock().unwrap().callbacks.created(&window_handle);
@ -483,6 +542,43 @@ impl WindowOpsMut for WindowInner {
);
}
fn set_window_position(&self, coords: ScreenPoint) {
// 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
// approach below will successfully move a window running
// under the crostini environment on a chromebook :-(
xcb_util::ewmh::request_move_resize_window(
self.conn.ewmh_conn(),
self.conn.screen_num,
self.window_id,
xcb::xproto::GRAVITY_STATIC,
1, // normal program
xcb_util::ewmh::MOVE_RESIZE_MOVE
| xcb_util::ewmh::MOVE_RESIZE_WINDOW_X
| xcb_util::ewmh::MOVE_RESIZE_WINDOW_Y,
coords.x as u32,
coords.y as u32,
// these dimensions are ignored because we're not
// passing the relevant MOVE_RESIZE_XX flags above,
// but are preserved here for clarity on what these
// parameters do
self.width as u32,
self.height as u32,
);
/*
xcb::configure_window(
self.conn.conn(),
self.window_id,
&[
(xcb::CONFIG_WINDOW_X as u16, coords.x as u32),
(xcb::CONFIG_WINDOW_Y as u16, coords.y as u32),
],
);
*/
}
/// Change the title for the window manager
fn set_title(&mut self, title: &str) {
xcb_util::icccm::set_wm_name(self.conn.conn(), self.window_id, title);
@ -540,6 +636,13 @@ impl WindowOps for Window {
})
}
fn set_window_position(&self, coords: ScreenPoint) -> Future<()> {
Connection::with_window_inner(self.0, move |inner| {
inner.set_window_position(coords);
Ok(())
})
}
fn apply<R, F: Send + 'static + Fn(&mut dyn Any, &dyn WindowOps) -> Fallible<R>>(
&self,
func: F,