Update SCTK to 0.18.0

The update is pretty minor, however we support now
`WindowEvent::Occluded` when xdg-shell v6 is available.

It also adds support for `Window::show_window_menu`.

Fixes #2927.
This commit is contained in:
Kirill Chibisov 2023-10-15 06:49:57 +04:00 committed by GitHub
parent 844269d017
commit b7e3649e8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 224 additions and 165 deletions

View file

@ -13,7 +13,7 @@ use xkbcommon_dl::{
XkbCommonCompose,
};
#[cfg(feature = "wayland")]
use {memmap2::MmapOptions, wayland_backend::io_lifetimes::OwnedFd};
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
#[cfg(feature = "x11")]
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};

View file

@ -444,7 +444,9 @@ impl Window {
}
#[inline]
pub fn show_window_menu(&self, _position: Position) {}
pub fn show_window_menu(&self, position: Position) {
x11_or_wayland!(match self; Window(w) => w.show_window_menu(position))
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {

View file

@ -11,8 +11,9 @@ use std::time::{Duration, Instant};
use sctk::reexports::calloop;
use sctk::reexports::calloop::Error as CalloopError;
use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::globals;
use sctk::reexports::client::{Connection, QueueHandle, WaylandSource};
use sctk::reexports::client::{Connection, QueueHandle};
use crate::dpi::{LogicalSize, PhysicalSize};
use crate::error::{EventLoopError, OsError as RootOsError};
@ -100,7 +101,7 @@ impl<T: 'static> EventLoop<T> {
)?;
// Register Wayland source.
let wayland_source = map_err!(WaylandSource::new(event_queue), WaylandError::Wire)?;
let wayland_source = WaylandSource::new(connection.clone(), event_queue);
let wayland_dispatcher =
calloop::Dispatcher::new(wayland_source, |_, queue, winit_state: &mut WinitState| {
let result = queue.dispatch_pending(winit_state);
@ -252,46 +253,7 @@ impl<T: 'static> EventLoop<T> {
let cause = loop {
let start = Instant::now();
// TODO(rib): remove this workaround and instead make sure that the calloop
// WaylandSource correctly implements the cooperative prepare_read protocol
// that support multithreaded wayland clients that may all read from the
// same socket.
//
// During the run of the user callback, some other code monitoring and reading the
// Wayland socket may have been run (mesa for example does this with vsync), if that
// is the case, some events may have been enqueued in our event queue.
//
// If some messages are there, the event loop needs to behave as if it was instantly
// woken up by messages arriving from the Wayland socket, to avoid delaying the
// dispatch of these events until we're woken up again.
let instant_wakeup = {
let mut wayland_source = self.wayland_dispatcher.as_source_mut();
let queue = wayland_source.queue();
let state = match &mut self.window_target.p {
PlatformEventLoopWindowTarget::Wayland(window_target) => {
window_target.state.get_mut()
}
#[cfg(x11_platform)]
_ => unreachable!(),
};
match queue.dispatch_pending(state) {
Ok(dispatched) => {
state.dispatched_events |= !state.events_sink.is_empty()
|| !state.window_compositor_updates.is_empty();
dispatched > 0
}
Err(error) => {
error!("Error dispatching wayland queue: {}", error);
self.set_exit_code(1);
return;
}
}
};
timeout = if instant_wakeup {
Some(Duration::ZERO)
} else {
timeout = {
let control_flow_timeout = match self.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO),
@ -305,7 +267,13 @@ impl<T: 'static> EventLoop<T> {
// NOTE Ideally we should flush as the last thing we do before polling
// to wait for events, and this should be done by the calloop
// WaylandSource but we currently need to flush writes manually.
let _ = self.connection.flush();
//
// Checking for flush error is essential to perform an exit with error, since
// once we have a protocol error, we could get stuck retrying...
if self.connection.flush().is_err() {
self.set_exit_code(1);
return;
}
if let Err(error) = self.loop_dispatch(timeout) {
// NOTE We exit on errors from dispatches, since if we've got protocol error

View file

@ -67,7 +67,11 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
};
// Drop the repeat, if there were any.
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
// The keyboard focus is considered as general focus.
state
@ -89,7 +93,11 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
// NOTE: we should drop the repeat regardless whethere it was for the present
// window of for the window which just went gone.
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
// NOTE: The check whether the window exists is essential as we might get a
// nil surface, regardless of what protocol says.
@ -204,6 +212,9 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
&& Some(key) == keyboard_state.current_repeat
{
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {
keyboard_state.loop_handle.remove(token);
}
}
}
WlKeyboardEvent::Modifiers {

View file

@ -2,7 +2,7 @@
use std::sync::Arc;
use fnv::FnvHashMap;
use ahash::AHashMap;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_touch::WlTouch;
@ -29,7 +29,7 @@ use keyboard::{KeyboardData, KeyboardState};
use text_input::TextInputData;
use touch::TouchPoint;
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct WinitSeatState {
/// The pointer bound on the seat.
pointer: Option<Arc<ThemedPointer<WinitPointerData>>>,
@ -38,7 +38,7 @@ pub struct WinitSeatState {
touch: Option<WlTouch>,
/// The mapping from touched points to the surfaces they're present.
touch_map: FnvHashMap<i32, TouchPoint>,
touch_map: AHashMap<i32, TouchPoint>,
/// The text input bound on the seat.
text_input: Option<Arc<ZwpTextInputV3>>,
@ -58,16 +58,7 @@ pub struct WinitSeatState {
impl WinitSeatState {
pub fn new() -> Self {
Self {
pointer: None,
touch: None,
relative_pointer: None,
text_input: None,
touch_map: Default::default(),
keyboard_state: None,
modifiers: ModifiersState::empty(),
modifiers_pending: false,
}
Default::default()
}
}
@ -97,12 +88,14 @@ impl SeatHandler for WinitState {
SeatCapability::Pointer if seat_state.pointer.is_none() => {
let surface = self.compositor_state.create_surface(queue_handle);
let surface_id = surface.id();
let pointer_data = WinitPointerData::new(seat.clone(), surface);
let pointer_data = WinitPointerData::new(seat.clone());
let themed_pointer = self
.seat_state
.get_pointer_with_theme_and_data(
queue_handle,
&seat,
self.shm.wl_shm(),
surface,
ThemeSpec::System,
pointer_data,
)
@ -167,7 +160,7 @@ impl SeatHandler for WinitState {
let pointer_data = pointer.pointer().winit_data();
// Remove the cursor from the mapping.
let surface_id = pointer_data.cursor_surface().id();
let surface_id = pointer.surface().id();
let _ = self.pointer_surfaces.remove(&surface_id);
// Remove the inner locks/confines before dropping the pointer.

View file

@ -2,6 +2,7 @@
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use sctk::reexports::client::delegate_dispatch;
use sctk::reexports::client::protocol::wl_pointer::WlPointer;
@ -10,15 +11,17 @@ use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy, QueueHandle, Dispatch};
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::csd_frame::FrameClick;
use sctk::compositor::SurfaceData;
use sctk::globals::GlobalData;
use sctk::seat::pointer::{PointerData, PointerDataExt};
use sctk::seat::pointer::{PointerEvent, PointerEventKind, PointerHandler};
use sctk::seat::SeatState;
use sctk::shell::xdg::frame::FrameClick;
use crate::dpi::{LogicalPosition, PhysicalPosition};
use crate::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
@ -67,35 +70,31 @@ impl PointerHandler for WinitState {
PointerEventKind::Enter { .. } | PointerEventKind::Motion { .. }
if parent_surface != surface =>
{
if let Some(icon) =
window.frame_point_moved(seat, surface, event.position.0, event.position.1)
{
if let Some(icon) = window.frame_point_moved(
seat,
surface,
Duration::ZERO,
event.position.0,
event.position.1,
) {
if let Some(pointer) = seat_state.pointer.as_ref() {
let surface = pointer
.pointer()
.data::<WinitPointerData>()
.unwrap()
.cursor_surface();
let scale_factor =
surface.data::<SurfaceData>().unwrap().scale_factor();
let _ = pointer.set_cursor(
connection,
icon,
self.shm.wl_shm(),
surface,
scale_factor,
);
let _ = pointer.set_cursor(connection, icon);
}
}
}
PointerEventKind::Leave { .. } if parent_surface != surface => {
window.frame_point_left();
}
ref kind @ PointerEventKind::Press { button, serial, .. }
| ref kind @ PointerEventKind::Release { button, serial, .. }
if parent_surface != surface =>
{
ref kind @ PointerEventKind::Press {
button,
serial,
time,
}
| ref kind @ PointerEventKind::Release {
button,
serial,
time,
} if parent_surface != surface => {
let click = match wayland_button_to_winit(button) {
MouseButton::Left => FrameClick::Normal,
MouseButton::Right => FrameClick::Alternate,
@ -109,6 +108,7 @@ impl PointerHandler for WinitState {
pressed,
seat,
serial,
Duration::from_millis(time as u64),
window_id,
&mut self.window_compositor_updates,
);
@ -238,9 +238,6 @@ impl PointerHandler for WinitState {
#[derive(Debug)]
pub struct WinitPointerData {
/// The surface associated with this pointer, which is used for icons.
cursor_surface: WlSurface,
/// The inner winit data associated with the pointer.
inner: Mutex<WinitPointerDataInner>,
@ -249,9 +246,8 @@ pub struct WinitPointerData {
}
impl WinitPointerData {
pub fn new(seat: WlSeat, surface: WlSurface) -> Self {
pub fn new(seat: WlSeat) -> Self {
Self {
cursor_surface: surface,
inner: Mutex::new(WinitPointerDataInner::default()),
sctk_data: PointerData::new(seat),
}
@ -313,11 +309,6 @@ impl WinitPointerData {
self.sctk_data.seat()
}
/// The WlSurface used to set cursor theme.
pub fn cursor_surface(&self) -> &WlSurface {
&self.cursor_surface
}
/// Active window.
pub fn focused_window(&self) -> Option<WindowId> {
self.inner.lock().unwrap().surface
@ -325,7 +316,7 @@ impl WinitPointerData {
/// Last button serial.
pub fn latest_button_serial(&self) -> u32 {
self.inner.lock().unwrap().latest_button_serial
self.sctk_data.latest_button_serial().unwrap_or_default()
}
/// Last enter serial.
@ -341,12 +332,6 @@ impl WinitPointerData {
}
}
impl Drop for WinitPointerData {
fn drop(&mut self) {
self.cursor_surface.destroy();
}
}
impl PointerDataExt for WinitPointerData {
fn pointer_data(&self) -> &PointerData {
&self.sctk_data
@ -486,7 +471,35 @@ impl Dispatch<ZwpConfinedPointerV1, GlobalData, WinitState> for PointerConstrain
}
}
impl Dispatch<WpCursorShapeDeviceV1, GlobalData, WinitState> for SeatState {
fn event(
_: &mut WinitState,
_: &WpCursorShapeDeviceV1,
_: <WpCursorShapeDeviceV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("wp_cursor_shape_manager has no events")
}
}
impl Dispatch<WpCursorShapeManagerV1, GlobalData, WinitState> for SeatState {
fn event(
_: &mut WinitState,
_: &WpCursorShapeManagerV1,
_: <WpCursorShapeManagerV1 as Proxy>::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<WinitState>,
) {
unreachable!("wp_cursor_device_manager has no events")
}
}
delegate_dispatch!(WinitState: [ WlPointer: WinitPointerData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeManagerV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeDeviceV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ZwpPointerConstraintsV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpLockedPointerV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpConfinedPointerV1: GlobalData] => PointerConstraintsState);

View file

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use fnv::FnvHashMap;
use ahash::AHashMap;
use sctk::reexports::calloop::LoopHandle;
use sctk::reexports::client::backend::ObjectId;
@ -62,10 +62,10 @@ pub struct WinitState {
pub xdg_shell: XdgShell,
/// The currently present windows.
pub windows: RefCell<FnvHashMap<WindowId, Arc<Mutex<WindowState>>>>,
pub windows: RefCell<AHashMap<WindowId, Arc<Mutex<WindowState>>>>,
/// The requests from the `Window` to EventLoop, such as close operations and redraw requests.
pub window_requests: RefCell<FnvHashMap<WindowId, Arc<WindowRequests>>>,
pub window_requests: RefCell<AHashMap<WindowId, Arc<WindowRequests>>>,
/// The events that were generated directly from the window.
pub window_events_sink: Arc<Mutex<EventSink>>,
@ -74,10 +74,10 @@ pub struct WinitState {
pub window_compositor_updates: Vec<WindowCompositorUpdate>,
/// Currently handled seats.
pub seats: FnvHashMap<ObjectId, WinitSeatState>,
pub seats: AHashMap<ObjectId, WinitSeatState>,
/// Currently present cursor surfaces.
pub pointer_surfaces: FnvHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
pub pointer_surfaces: AHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
/// The state of the text input on the client.
pub text_input_state: Option<TextInputState>,
@ -136,7 +136,7 @@ impl WinitState {
let seat_state = SeatState::new(globals, queue_handle);
let mut seats = FnvHashMap::default();
let mut seats = AHashMap::default();
for seat in seat_state.seats() {
seats.insert(seat.id(), WinitSeatState::new());
}
@ -287,7 +287,12 @@ impl WindowHandler for WinitState {
.expect("got configure for dead window.")
.lock()
.unwrap()
.configure(configure, &self.shm, &self.subcompositor_state);
.configure(
configure,
&self.shm,
&self.subcompositor_state,
&mut self.events_sink,
);
self.window_compositor_updates[pos].size = Some(new_size);
}
@ -325,6 +330,16 @@ impl OutputHandler for WinitState {
}
impl CompositorHandler for WinitState {
fn transform_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wayland_client::protocol::wl_surface::WlSurface,
_: wayland_client::protocol::wl_output::Transform,
) {
// TODO(kchibisov) we need to expose it somehow in winit.
}
fn scale_factor_changed(
&mut self,
_: &Connection,

View file

@ -374,6 +374,13 @@ impl Window {
None
}
#[inline]
pub fn show_window_menu(&self, position: Position) {
let scale_factor = self.scale_factor();
let position = position.to_logical(scale_factor);
self.window_state.lock().unwrap().show_window_menu(position);
}
#[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.window_state

View file

@ -3,6 +3,7 @@
use std::mem::ManuallyDrop;
use std::num::NonZeroU32;
use std::sync::{Arc, Weak};
use std::time::Duration;
use log::{info, warn};
@ -10,14 +11,16 @@ use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_shm::WlShm;
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::csd_frame::{
DecorationsFrame, FrameAction, FrameClick, ResizeEdge, WindowState as XdgWindowState,
};
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge;
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge;
use sctk::compositor::{CompositorState, Region, SurfaceData};
use sctk::compositor::{CompositorState, Region};
use sctk::seat::pointer::ThemedPointer;
use sctk::shell::xdg::frame::{DecorationsFrame, FrameAction, FrameClick};
use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure};
use sctk::shell::xdg::XdgSurface;
use sctk::shell::WaylandSurface;
@ -27,6 +30,9 @@ use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::error::{ExternalError, NotSupportedError};
use crate::event::WindowEvent;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::make_wid;
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::WindowId;
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
@ -39,7 +45,7 @@ use crate::platform_impl::wayland::state::{WindowCompositorUpdate, WinitState};
#[cfg(feature = "sctk-adwaita")]
pub type WinitFrame = sctk_adwaita::AdwaitaFrame<WinitState>;
#[cfg(not(feature = "sctk-adwaita"))]
pub type WinitFrame = sctk::shell::xdg::frame::fallback_frame::FallbackFrame<WinitState>;
pub type WinitFrame = sctk::shell::xdg::fallback_frame::FallbackFrame<WinitState>;
// Minimum window inner size.
const MIN_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(2, 1);
@ -245,6 +251,7 @@ impl WindowState {
configure: WindowConfigure,
shm: &Shm,
subcompositor: &Arc<SubcompositorState>,
event_sink: &mut EventSink,
) -> LogicalSize<u32> {
if configure.decoration_mode == DecorationMode::Client
&& self.frame.is_none()
@ -260,6 +267,7 @@ impl WindowState {
) {
Ok(mut frame) => {
frame.set_title(&self.title);
frame.set_scaling_factor(self.scale_factor);
// Hide the frame if we were asked to not decorate.
frame.set_hidden(!self.decorate);
self.frame = Some(frame);
@ -276,6 +284,19 @@ impl WindowState {
let stateless = Self::is_stateless(&configure);
// Emit `Occluded` event on suspension change.
let occluded = configure.state.contains(XdgWindowState::SUSPENDED);
if self
.last_configure
.as_ref()
.map(|c| c.state.contains(XdgWindowState::SUSPENDED))
.unwrap_or(false)
!= occluded
{
let window_id = make_wid(self.window.wl_surface());
event_sink.push_window_event(WindowEvent::Occluded(occluded), window_id);
}
let new_size = if let Some(frame) = self.frame.as_mut() {
// Configure the window states.
frame.update_state(configure.state);
@ -342,23 +363,40 @@ impl WindowState {
}
/// Tells whether the window should be closed.
#[allow(clippy::too_many_arguments)]
pub fn frame_click(
&mut self,
click: FrameClick,
pressed: bool,
seat: &WlSeat,
serial: u32,
timestamp: Duration,
window_id: WindowId,
updates: &mut Vec<WindowCompositorUpdate>,
) -> Option<bool> {
match self.frame.as_mut()?.on_click(click, pressed)? {
match self.frame.as_mut()?.on_click(timestamp, click, pressed)? {
FrameAction::Minimize => self.window.set_minimized(),
FrameAction::Maximize => self.window.set_maximized(),
FrameAction::UnMaximize => self.window.unset_maximized(),
FrameAction::Close => WinitState::queue_close(updates, window_id),
FrameAction::Move => self.has_pending_move = Some(serial),
FrameAction::Resize(edge) => self.window.resize(seat, serial, edge),
FrameAction::Resize(edge) => {
let edge = match edge {
ResizeEdge::None => XdgResizeEdge::None,
ResizeEdge::Top => XdgResizeEdge::Top,
ResizeEdge::Bottom => XdgResizeEdge::Bottom,
ResizeEdge::Left => XdgResizeEdge::Left,
ResizeEdge::TopLeft => XdgResizeEdge::TopLeft,
ResizeEdge::BottomLeft => XdgResizeEdge::BottomLeft,
ResizeEdge::Right => XdgResizeEdge::Right,
ResizeEdge::TopRight => XdgResizeEdge::TopRight,
ResizeEdge::BottomRight => XdgResizeEdge::BottomRight,
_ => return None,
};
self.window.resize(seat, serial, edge);
}
FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)),
_ => (),
};
Some(false)
@ -375,14 +413,15 @@ impl WindowState {
&mut self,
seat: &WlSeat,
surface: &WlSurface,
timestamp: Duration,
x: f64,
y: f64,
) -> Option<&str> {
) -> Option<CursorIcon> {
// Take the serial if we had any, so it doesn't stick around.
let serial = self.has_pending_move.take();
if let Some(frame) = self.frame.as_mut() {
let cursor = frame.click_point_moved(surface, x, y);
let cursor = frame.click_point_moved(timestamp, &surface.id(), x, y);
// If we have a cursor change, that means that cursor is over the decorations,
// so try to apply move.
if let Some(serial) = cursor.is_some().then_some(serial).flatten() {
@ -498,14 +537,12 @@ impl WindowState {
/// Refresh the decorations frame if it's present returning whether the client should redraw.
pub fn refresh_frame(&mut self) -> bool {
if let Some(frame) = self.frame.as_mut() {
let dirty = frame.is_dirty();
if dirty {
frame.draw();
if frame.is_dirty() {
return frame.draw();
}
dirty
} else {
false
}
false
}
/// Reload the cursor style on the given window.
@ -592,20 +629,8 @@ impl WindowState {
return;
}
self.apply_on_poiner(|pointer, data| {
let surface = data.cursor_surface();
let scale_factor = surface.data::<SurfaceData>().unwrap().scale_factor();
if pointer
.set_cursor(
&self.connection,
cursor_icon.name(),
&self.shm,
surface,
scale_factor,
)
.is_err()
{
self.apply_on_poiner(|pointer, _| {
if pointer.set_cursor(&self.connection, cursor_icon).is_err() {
warn!("Failed to set cursor to {:?}", cursor_icon);
}
})
@ -709,6 +734,15 @@ impl WindowState {
Ok(())
}
pub fn show_window_menu(&self, position: LogicalPosition<u32>) {
// TODO(kchibisov) handle touch serials.
self.apply_on_poiner(|_, data| {
let serial = data.latest_button_serial();
let seat = data.seat();
self.window.show_window_menu(seat, serial, position.into());
});
}
/// Set the position of the cursor.
pub fn set_cursor_position(&self, position: LogicalPosition<f64>) -> Result<(), ExternalError> {
if self.pointer_constraints.is_none() {
@ -844,6 +878,10 @@ impl WindowState {
if self.fractional_scale.is_none() {
let _ = self.window.set_buffer_scale(self.scale_factor as _);
}
if let Some(frame) = self.frame.as_mut() {
frame.set_scaling_factor(scale_factor);
}
}
/// Make window background blurred
@ -926,10 +964,20 @@ impl Drop for WindowState {
ManuallyDrop::drop(&mut self.window);
}
if let Some(blur) = &self.blur {
// Cleanup objects.
if let Some(blur) = self.blur.take() {
blur.release();
}
if let Some(fs) = self.fractional_scale.take() {
fs.destroy();
}
if let Some(viewport) = self.viewport.take() {
viewport.destroy();
}
surface.destroy();
}
}
@ -965,17 +1013,17 @@ pub enum FrameCallbackState {
Received,
}
impl From<ResizeDirection> for ResizeEdge {
impl From<ResizeDirection> for XdgResizeEdge {
fn from(value: ResizeDirection) -> Self {
match value {
ResizeDirection::North => ResizeEdge::Top,
ResizeDirection::West => ResizeEdge::Left,
ResizeDirection::NorthWest => ResizeEdge::TopLeft,
ResizeDirection::NorthEast => ResizeEdge::TopRight,
ResizeDirection::East => ResizeEdge::Right,
ResizeDirection::SouthWest => ResizeEdge::BottomLeft,
ResizeDirection::SouthEast => ResizeEdge::BottomRight,
ResizeDirection::South => ResizeEdge::Bottom,
ResizeDirection::North => XdgResizeEdge::Top,
ResizeDirection::West => XdgResizeEdge::Left,
ResizeDirection::NorthWest => XdgResizeEdge::TopLeft,
ResizeDirection::NorthEast => XdgResizeEdge::TopRight,
ResizeDirection::East => XdgResizeEdge::Right,
ResizeDirection::SouthWest => XdgResizeEdge::BottomLeft,
ResizeDirection::SouthEast => XdgResizeEdge::BottomRight,
ResizeDirection::South => XdgResizeEdge::Bottom,
}
}
}

View file

@ -32,7 +32,7 @@ use std::{
ops::Deref,
os::{
raw::*,
unix::io::{AsRawFd, RawFd},
unix::io::{AsRawFd, BorrowedFd},
},
ptr,
rc::Rc,
@ -82,7 +82,7 @@ use crate::{
const ALL_DEVICES: u16 = 0;
const ALL_MASTER_DEVICES: u16 = 1;
type X11Source = Generic<RawFd>;
type X11Source = Generic<BorrowedFd<'static>>;
struct WakeSender<T> {
sender: Sender<T>,
@ -268,7 +268,8 @@ impl<T: 'static> EventLoop<T> {
// Create the X11 event dispatcher.
let source = X11Source::new(
xconn.xcb_connection().as_raw_fd(),
// SAFETY: xcb owns the FD and outlives the source.
unsafe { BorrowedFd::borrow_raw(xconn.xcb_connection().as_raw_fd()) },
calloop::Interest::READ,
calloop::Mode::Level,
);

View file

@ -1636,6 +1636,9 @@ impl UnownedWindow {
self.drag_initiate(util::MOVERESIZE_MOVE)
}
#[inline]
pub fn show_window_menu(&self, _position: Position) {}
/// Resizes the window while it is being dragged.
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.drag_initiate(match direction {