mirror of
https://github.com/wez/wezterm.git
synced 2024-10-26 15:52:29 +03:00
Better decorations
This commit is contained in:
parent
889f8a9cd7
commit
0c97ace3cb
@ -2,6 +2,8 @@
|
||||
//! in smithay_client_toolkit 0.11 which is Copyright (c) 2018 Victor Berger
|
||||
//! and provided under the terms of the MIT license.
|
||||
|
||||
// TODO: update this for SCTK 0.18 and use this instead of the FallbackFrame
|
||||
|
||||
use crate::os::wayland::pointer::make_theme_manager;
|
||||
use config::{ConfigHandle, RgbaColor, WindowFrameConfig};
|
||||
use smithay_client_toolkit::output::{add_output_listener, with_output_info, OutputListener};
|
||||
|
@ -218,33 +218,35 @@ impl WaylandState {
|
||||
let wid = SurfaceUserData::from_wl(parent_surface).window_id;
|
||||
let mut inner = windows.get(&wid).unwrap().borrow_mut();
|
||||
|
||||
match evt.kind {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
inner.window_frame.click_point_moved(&evt.surface, x, y);
|
||||
}
|
||||
PointerEventKind::Leave { .. } => {
|
||||
inner.window_frame.click_point_left();
|
||||
}
|
||||
PointerEventKind::Motion { .. } => {
|
||||
inner.window_frame.click_point_moved(&evt.surface, x, y);
|
||||
}
|
||||
PointerEventKind::Press { button, serial, .. }
|
||||
| PointerEventKind::Release { button, serial, .. } => {
|
||||
let pressed = if matches!(evt.kind, PointerEventKind::Press { .. }) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let click = match button {
|
||||
0x110 => FrameClick::Normal,
|
||||
0x111 => FrameClick::Alternate,
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(action) = inner.window_frame.on_click(click, pressed) {
|
||||
inner.frame_action(pointer, serial, action);
|
||||
if let Some(frame) = inner.decorations.as_mut() {
|
||||
match evt.kind {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
frame.click_point_moved(&evt.surface, x, y);
|
||||
}
|
||||
PointerEventKind::Leave { .. } => {
|
||||
frame.click_point_left();
|
||||
}
|
||||
PointerEventKind::Motion { .. } => {
|
||||
frame.click_point_moved(&evt.surface, x, y);
|
||||
}
|
||||
PointerEventKind::Press { button, serial, .. }
|
||||
| PointerEventKind::Release { button, serial, .. } => {
|
||||
let pressed = if matches!(evt.kind, PointerEventKind::Press { .. }) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let click = match button {
|
||||
0x110 => FrameClick::Normal,
|
||||
0x111 => FrameClick::Alternate,
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(action) = frame.on_click(click, pressed) {
|
||||
inner.frame_action(pointer, serial, action);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,54 +211,33 @@ impl WaylandWindow {
|
||||
dpi: config.dpi.unwrap_or(crate::DEFAULT_DPI) as usize,
|
||||
};
|
||||
|
||||
let decorations = config.window_decorations;
|
||||
let decorations_mode = if decorations == WindowDecorations::NONE {
|
||||
Decorations::None
|
||||
} else if decorations == WindowDecorations::default() {
|
||||
Decorations::ServerDefault
|
||||
} else {
|
||||
// SCTK/Wayland don't allow more nuance than "decorations are hidden",
|
||||
// so if we have a mixture of things, then we need to force our
|
||||
// client side decoration rendering.
|
||||
Decorations::RequestClient
|
||||
};
|
||||
|
||||
let window = {
|
||||
let xdg_shell = &conn.wayland_state.borrow().xdg;
|
||||
xdg_shell.create_window(surface.clone(), Decorations::RequestServer, &qh)
|
||||
xdg_shell.create_window(surface.clone(), decorations_mode, &qh)
|
||||
};
|
||||
|
||||
window.set_app_id(class_name.to_string());
|
||||
window.set_title(name.to_string());
|
||||
let decorations = config.window_decorations;
|
||||
|
||||
let decor_mode = if decorations == WindowDecorations::NONE {
|
||||
None
|
||||
} else if decorations == WindowDecorations::default() {
|
||||
Some(DecorationMode::Server)
|
||||
} else {
|
||||
Some(DecorationMode::Client)
|
||||
};
|
||||
window.request_decoration_mode(decor_mode);
|
||||
|
||||
let mut window_frame = {
|
||||
let wayland_state = &conn.wayland_state.borrow();
|
||||
let shm = &wayland_state.shm;
|
||||
let subcompositor = wayland_state.subcompositor.clone();
|
||||
FallbackFrame::new(&window, shm, subcompositor, qh.clone())
|
||||
.expect("failed to create csd frame")
|
||||
};
|
||||
let hidden = match decor_mode {
|
||||
Some(DecorationMode::Client) => false,
|
||||
_ => true,
|
||||
};
|
||||
window_frame.set_hidden(hidden);
|
||||
if !hidden {
|
||||
window_frame.resize(
|
||||
NonZeroU32::new(dimensions.pixel_width as u32)
|
||||
.ok_or_else(|| anyhow!("dimensions {dimensions:?} are invalid"))?,
|
||||
NonZeroU32::new(dimensions.pixel_height as u32)
|
||||
.ok_or_else(|| anyhow!("dimensions {dimensions:?} are invalid"))?,
|
||||
);
|
||||
}
|
||||
|
||||
window.set_min_size(Some((32, 32)));
|
||||
let (w, h) = window_frame.add_borders(
|
||||
window.set_window_geometry(
|
||||
0,
|
||||
0,
|
||||
dimensions.pixel_width as u32,
|
||||
dimensions.pixel_height as u32,
|
||||
);
|
||||
let (x, y) = window_frame.location();
|
||||
window
|
||||
.xdg_surface()
|
||||
.set_window_geometry(x, y, w as i32, h as i32);
|
||||
window.commit();
|
||||
|
||||
let copy_and_paste = CopyAndPaste::create();
|
||||
@ -275,7 +254,7 @@ impl WaylandWindow {
|
||||
copy_and_paste,
|
||||
invalidated: false,
|
||||
window: Some(window),
|
||||
window_frame,
|
||||
decorations: None,
|
||||
dimensions,
|
||||
resize_increments: None,
|
||||
window_state: WindowState::default(),
|
||||
@ -456,7 +435,6 @@ impl WindowOps for WaylandWindow {
|
||||
pub(crate) struct PendingEvent {
|
||||
pub(crate) close: bool,
|
||||
pub(crate) had_configure_event: bool,
|
||||
refresh_decorations: bool,
|
||||
// XXX: configure and window_configure could probably be combined, but right now configure only
|
||||
// queues a new size, so it can be out of sync. Example would be maximizing and minimizing winodw
|
||||
pub(crate) configure: Option<(u32, u32)>,
|
||||
@ -501,7 +479,7 @@ pub struct WaylandWindowInner {
|
||||
surface_factor: f64,
|
||||
copy_and_paste: Arc<Mutex<CopyAndPaste>>,
|
||||
window: Option<XdgWindow>,
|
||||
pub(super) window_frame: FallbackFrame<WaylandState>,
|
||||
pub(super) decorations: Option<FallbackFrame<WaylandState>>,
|
||||
dimensions: Dimensions,
|
||||
resize_increments: Option<ResizeIncrement>,
|
||||
window_state: WindowState,
|
||||
@ -536,6 +514,7 @@ impl WaylandWindowInner {
|
||||
fn close(&mut self) {
|
||||
self.events.dispatch(WindowEvent::Destroyed);
|
||||
self.window.take();
|
||||
self.decorations.take();
|
||||
}
|
||||
|
||||
fn show(&mut self) {
|
||||
@ -548,11 +527,10 @@ impl WaylandWindowInner {
|
||||
}
|
||||
|
||||
fn refresh_frame(&mut self) {
|
||||
if let Some(window) = self.window.as_mut() {
|
||||
if self.window_frame.is_dirty() && !self.window_frame.is_hidden() {
|
||||
self.window_frame.draw();
|
||||
if let Some(frame) = self.decorations.as_mut() {
|
||||
if frame.is_dirty() && !frame.is_hidden() {
|
||||
frame.draw();
|
||||
}
|
||||
window.wl_surface().commit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -735,7 +713,7 @@ impl WaylandWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_pending_event(&mut self) {
|
||||
fn dispatch_pending_event(&mut self) {
|
||||
let mut pending;
|
||||
{
|
||||
let mut pending_events = self.pending_event.lock().unwrap();
|
||||
@ -758,7 +736,6 @@ impl WaylandWindowInner {
|
||||
|
||||
if pending.configure.is_none() {
|
||||
if pending.dpi.is_some() {
|
||||
// Synthesize a pending configure event for the dpi change
|
||||
pending.configure.replace((
|
||||
self.pixels_to_surface(self.dimensions.pixel_width as i32) as u32,
|
||||
self.pixels_to_surface(self.dimensions.pixel_height as i32) as u32,
|
||||
@ -767,11 +744,49 @@ impl WaylandWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref window_config) = pending.window_configure {
|
||||
self.window_frame.update_state(window_config.state);
|
||||
self.window_frame
|
||||
.update_wm_capabilities(window_config.capabilities);
|
||||
}
|
||||
let conn = Connection::get().unwrap().wayland();
|
||||
|
||||
if let Some(window_config) = pending.window_configure {
|
||||
if self.window.is_some()
|
||||
&& window_config.decoration_mode == DecorationMode::Client
|
||||
&& self.config.window_decorations != WindowDecorations::NONE
|
||||
{
|
||||
log::trace!("Client side decoration");
|
||||
// the server requested client side decoration
|
||||
// create a frame, if we don't have one already
|
||||
let title = self.title.as_ref();
|
||||
let window = self.window.as_ref();
|
||||
let decorations = self.decorations.get_or_insert_with(|| {
|
||||
let state = conn.wayland_state.borrow();
|
||||
let qh = WaylandConnection::get()
|
||||
.unwrap()
|
||||
.wayland()
|
||||
.event_queue
|
||||
.borrow()
|
||||
.handle()
|
||||
.clone();
|
||||
let mut frame = FallbackFrame::new(
|
||||
window.unwrap(),
|
||||
&state.shm,
|
||||
state.subcompositor.clone(),
|
||||
qh,
|
||||
)
|
||||
.expect("failed to create csd frame.");
|
||||
if let Some(title) = title {
|
||||
frame.set_title(title.clone());
|
||||
}
|
||||
frame.into()
|
||||
});
|
||||
decorations.set_hidden(false);
|
||||
decorations.update_state(window_config.state);
|
||||
decorations.update_wm_capabilities(window_config.capabilities);
|
||||
} else {
|
||||
if let Some(frame) = self.decorations.as_mut() {
|
||||
// If we have a frame already, hide it.
|
||||
frame.set_hidden(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some((mut w, mut h)) = pending.configure.take() {
|
||||
log::trace!("Pending configure: w:{w}, h{h} -- {:?}", self.window);
|
||||
@ -786,11 +801,29 @@ impl WaylandWindowInner {
|
||||
// Do this early because this affects surface_to_pixels/pixels_to_surface
|
||||
self.dimensions.dpi = dpi;
|
||||
|
||||
// we need to subtract the decorations before trying to resize
|
||||
const MIN_PIXELS: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) };
|
||||
if let Some(ref dec) = self.decorations {
|
||||
if !dec.is_hidden() {
|
||||
let inner_size = dec.subtract_borders(
|
||||
NonZeroU32::new(w).unwrap(),
|
||||
NonZeroU32::new(h).unwrap(),
|
||||
);
|
||||
// Clamp the size to at least one pixel.
|
||||
let inner_width = inner_size.0.unwrap_or(MIN_PIXELS);
|
||||
let inner_height = inner_size.1.unwrap_or(MIN_PIXELS);
|
||||
w = inner_width.get();
|
||||
h = inner_height.get();
|
||||
}
|
||||
}
|
||||
|
||||
let mut pixel_width = self.surface_to_pixels(w.try_into().unwrap());
|
||||
let mut pixel_height = self.surface_to_pixels(h.try_into().unwrap());
|
||||
|
||||
if self.window_state.can_resize() {
|
||||
self.window_frame.set_resizable(true);
|
||||
if let Some(ref mut dec) = self.decorations {
|
||||
dec.set_resizable(true);
|
||||
}
|
||||
if let Some(incr) = self.resize_increments {
|
||||
let min_width = incr.base_width + incr.x;
|
||||
let min_height = incr.base_height + incr.y;
|
||||
@ -801,29 +834,33 @@ impl WaylandWindowInner {
|
||||
max(pixel_height - extra_height, min_height as i32);
|
||||
w = self.pixels_to_surface(desired_pixel_width) as u32;
|
||||
h = self.pixels_to_surface(desired_pixel_height) as u32;
|
||||
pixel_width = self.surface_to_pixels(w.try_into().unwrap());
|
||||
pixel_height = self.surface_to_pixels(h.try_into().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("Resizing frame");
|
||||
let (width, height) = self.window_frame.subtract_borders(
|
||||
NonZeroU32::new(pixel_width as u32).unwrap(),
|
||||
NonZeroU32::new(pixel_height as u32).unwrap(),
|
||||
);
|
||||
// Clamp the size to at least one pixel.
|
||||
let width = width.unwrap_or(NonZeroU32::new(1).unwrap());
|
||||
let height = height.unwrap_or(NonZeroU32::new(1).unwrap());
|
||||
if !self.window_frame.is_hidden() {
|
||||
self.window_frame.resize(width, height);
|
||||
let window = self.window.as_ref().unwrap();
|
||||
|
||||
match self.decorations.as_mut() {
|
||||
Some(frame) if !frame.is_hidden() => {
|
||||
log::trace!("Resizing frame");
|
||||
frame.resize(w.try_into().unwrap(), h.try_into().unwrap());
|
||||
let outer_size = frame.add_borders(w, h);
|
||||
let (x, y) = frame.location();
|
||||
window.set_window_geometry(
|
||||
x.try_into().unwrap(),
|
||||
y.try_into().unwrap(),
|
||||
outer_size.0,
|
||||
outer_size.1,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
window.set_window_geometry(0, 0, w, h);
|
||||
}
|
||||
}
|
||||
let (x, y) = self.window_frame.location();
|
||||
let outer_size = self.window_frame.add_borders(width.get(), height.get());
|
||||
self.window
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.xdg_surface()
|
||||
.set_window_geometry(x, y, outer_size.0 as i32, outer_size.1 as i32);
|
||||
// recompute the pixel dimensions because they may have changed
|
||||
// due to resizing or decorations
|
||||
pixel_width = self.surface_to_pixels(w.try_into().unwrap());
|
||||
pixel_height = self.surface_to_pixels(h.try_into().unwrap());
|
||||
|
||||
// Compute the new pixel dimensions
|
||||
let new_dimensions = Dimensions {
|
||||
pixel_width: pixel_width.try_into().unwrap(),
|
||||
@ -859,8 +896,7 @@ impl WaylandWindowInner {
|
||||
wegl_surface.resize(pixel_width, pixel_height, 0, 0);
|
||||
}
|
||||
if self.surface_factor != factor {
|
||||
let wayland_conn = Connection::get().unwrap().wayland();
|
||||
let wayland_state = wayland_conn.wayland_state.borrow();
|
||||
let wayland_state = conn.wayland_state.borrow();
|
||||
let mut pool = wayland_state.mem_pool.borrow_mut();
|
||||
|
||||
// Make a "fake" buffer with the right dimensions, as
|
||||
@ -882,9 +918,6 @@ impl WaylandWindowInner {
|
||||
self.do_paint().unwrap();
|
||||
}
|
||||
}
|
||||
if pending.refresh_decorations && self.window.is_some() {
|
||||
self.refresh_frame();
|
||||
}
|
||||
if pending.had_configure_event && self.window.is_some() {
|
||||
log::debug!("Had configured an event");
|
||||
if let Some(notify) = self.pending_first_configure.take() {
|
||||
@ -967,6 +1000,9 @@ impl WaylandWindowInner {
|
||||
if let Some(window) = self.window.as_ref() {
|
||||
window.set_title(title.clone());
|
||||
}
|
||||
if let Some(frame) = self.decorations.as_mut() {
|
||||
frame.set_title(title.clone());
|
||||
}
|
||||
self.refresh_frame();
|
||||
self.title = Some(title);
|
||||
}
|
||||
@ -1157,7 +1193,7 @@ impl WaylandState {
|
||||
self.windows.borrow().get(&window_id).map(Rc::clone)
|
||||
}
|
||||
|
||||
fn handle_window_event(&self, window: &XdgWindow, event: WaylandWindowEvent) {
|
||||
fn handle_window_event(&mut self, window: &XdgWindow, event: WaylandWindowEvent) {
|
||||
let surface_data = SurfaceUserData::from_wl(window.wl_surface());
|
||||
let window_id = surface_data.window_id;
|
||||
|
||||
@ -1180,12 +1216,12 @@ impl WaylandState {
|
||||
}
|
||||
}
|
||||
WaylandWindowEvent::Request(configure) => {
|
||||
pending_event.window_configure.replace(configure.clone());
|
||||
// TODO: This should the new queue function
|
||||
// p.queue_configure(&configure)
|
||||
//
|
||||
let mut changed;
|
||||
pending_event.had_configure_event = true;
|
||||
|
||||
if let (Some(w), Some(h)) = configure.new_size {
|
||||
changed = pending_event.configure.is_none();
|
||||
pending_event.configure.replace((w.get(), h.get()));
|
||||
@ -1216,8 +1252,12 @@ impl WaylandState {
|
||||
if pending_event.window_state.is_none() && state != WindowState::default() {
|
||||
changed = true;
|
||||
}
|
||||
if pending_event.window_configure.is_none() {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
pending_event.window_state.replace(state);
|
||||
pending_event.window_configure.replace(configure);
|
||||
changed
|
||||
}
|
||||
};
|
||||
@ -1238,6 +1278,14 @@ impl CompositorHandler for WaylandState {
|
||||
_surface: &wayland_client::protocol::wl_surface::WlSurface,
|
||||
_new_factor: i32,
|
||||
) {
|
||||
// TODO: uncomment after upgrading SCTK to 0.18
|
||||
// let window_id = SurfaceUserData::from_wl(surface).window_id;
|
||||
// WaylandConnection::with_window_inner(window_id, |inner| {
|
||||
// if let Some(frame) = inner.window_frame.as_mut() {
|
||||
// frame.set_scaling_factor(new_factor as f64);
|
||||
// }
|
||||
// Ok(())
|
||||
// });
|
||||
// We do nothing, we get the scale_factor from surface_data
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user