Implement DPI Usability Upgrades for X11 and Wayland (#1098)

* Fix compile errors

* Use `mio` for the X11 event loop

* Removes `calloop` from the X11 event loop, as the method of draining a
  source using a closure provided to the `calloop::EventLoop` instance
  conflicts with the need to deliver events directly to the callback
  provided to `EventLoop::run`, in order to respond to the value provided by
  `WindowEvent::HiDpiFactorChanged`.

* Implement interactive `HiDpiFactorChanged` event for X11

* Implement interactive `HiDpiFactorChanged` event for Wayland

* Run cargo fmt

* Fix Wayland not processing events from EventQueue

* Backport #981
This commit is contained in:
Murarth 2020-01-04 01:31:23 -05:00 committed by Osspial
parent 6bb7db7c11
commit 7b43b0bc94
12 changed files with 563 additions and 627 deletions

View file

@ -1,4 +1,4 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice};
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
@ -11,7 +11,7 @@ use super::{
use util::modifiers::{ModifierKeyState, ModifierKeymap};
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{PhysicalPosition, PhysicalSize},
event::{
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent,
},
@ -45,7 +45,7 @@ impl<T: 'static> EventProcessor<T> {
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
where
F: Fn(&UnownedWindow) -> Ret,
F: Fn(&Arc<UnownedWindow>) -> Ret,
{
let mut deleted = false;
let window_id = WindowId(window_id);
@ -59,7 +59,7 @@ impl<T: 'static> EventProcessor<T> {
deleted = arc.is_none();
arc
})
.map(|window| callback(&*window));
.map(|window| callback(&window));
if deleted {
// Garbage collection
wt.windows.borrow_mut().remove(&window_id);
@ -107,7 +107,7 @@ impl<T: 'static> EventProcessor<T> {
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
where
F: FnMut(Event<T>),
F: FnMut(Event<'_, T>),
{
let wt = get_xtarget(&self.target);
// XFilterEvent tells us when an event has been discarded by the input method.
@ -321,16 +321,11 @@ impl<T: 'static> EventProcessor<T> {
}
ffi::ConfigureNotify => {
#[derive(Debug, Default)]
struct Events {
resized: Option<WindowEvent>,
moved: Option<WindowEvent>,
dpi_changed: Option<WindowEvent>,
}
let xev: &ffi::XConfigureEvent = xev.as_ref();
let xwindow = xev.window;
let events = self.with_window(xwindow, |window| {
let window_id = mkwid(xwindow);
if let Some(window) = self.with_window(xwindow, Arc::clone) {
// So apparently...
// `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
// `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
@ -344,7 +339,6 @@ impl<T: 'static> EventProcessor<T> {
let new_inner_size = (xev.width as u32, xev.height as u32);
let new_inner_position = (xev.x as i32, xev.y as i32);
let mut monitor = window.current_monitor(); // This must be done *before* locking!
let mut shared_state_lock = window.shared_state.lock();
let (mut resized, moved) = {
@ -374,8 +368,6 @@ impl<T: 'static> EventProcessor<T> {
(resized, moved)
};
let mut events = Events::default();
let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position.
let frame_extents = shared_state_lock
@ -392,9 +384,10 @@ impl<T: 'static> EventProcessor<T> {
.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
shared_state_lock.position = Some(outer);
if moved {
let logical_position =
LogicalPosition::from_physical(outer, monitor.hidpi_factor);
events.moved = Some(WindowEvent::Moved(logical_position));
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Moved(outer.into()),
});
}
outer
} else {
@ -406,36 +399,46 @@ impl<T: 'static> EventProcessor<T> {
// resizing by dragging across monitors *without* dropping the window.
let (width, height) = shared_state_lock
.dpi_adjusted
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
.unwrap_or_else(|| (xev.width as u32, xev.height as u32));
let last_hidpi_factor = shared_state_lock.last_monitor.hidpi_factor;
let new_hidpi_factor = {
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
let new_monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
let monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
if new_monitor.is_dummy() {
if monitor.is_dummy() {
// Avoid updating monitor using a dummy monitor handle
last_hidpi_factor
} else {
monitor = new_monitor;
shared_state_lock.last_monitor = monitor.clone();
monitor.hidpi_factor
}
};
if last_hidpi_factor != new_hidpi_factor {
events.dpi_changed =
Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
let (new_width, new_height, flusher) = window.adjust_for_dpi(
let (new_width, new_height) = window.adjust_for_dpi(
last_hidpi_factor,
new_hidpi_factor,
width,
height,
);
flusher.queue();
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
let mut new_inner_size = Some(PhysicalSize::new(new_width, new_height));
callback(Event::WindowEvent {
window_id,
event: WindowEvent::HiDpiFactorChanged {
hidpi_factor: new_hidpi_factor,
new_inner_size: &mut new_inner_size,
},
});
if let Some(new_size) = new_inner_size {
window.set_inner_size_physical(new_size.width, new_size.height);
shared_state_lock.dpi_adjusted = Some(new_size.into());
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
}
}
}
@ -444,44 +447,19 @@ impl<T: 'static> EventProcessor<T> {
// WMs constrain the window size, making the resize fail. This would cause an endless stream of
// XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (
adjusted_size.0.round() as u32,
adjusted_size.1.round() as u32,
);
if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(wt.xconn.xlib.XResizeWindow)(
wt.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
window.set_inner_size_physical(adjusted_size.0, adjusted_size.1);
}
}
if resized {
let logical_size =
LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
events
});
if let Some(events) = events {
let window_id = mkwid(xwindow);
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.resized {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.moved {
callback(Event::WindowEvent { window_id, event });
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Resized(new_inner_size.into()),
});
}
}
}
@ -728,24 +706,17 @@ impl<T: 'static> EventProcessor<T> {
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
});
if cursor_moved == Some(true) {
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id,
position,
modifiers,
},
});
} else {
return;
}
let position =
PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id,
position,
modifiers,
},
});
} else if cursor_moved.is_none() {
return;
}
@ -836,18 +807,14 @@ impl<T: 'static> EventProcessor<T> {
}
}
if let Some(dpi_factor) =
self.with_window(xev.event, |window| window.hidpi_factor())
{
if self.window_exists(xev.event) {
callback(Event::WindowEvent {
window_id,
event: CursorEntered { device_id },
});
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let position =
PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
// The mods field on this event isn't actually populated, so query the
// pointer device. In the future, we can likely remove this round-trip by
@ -890,11 +857,6 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_FocusIn => {
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
let dpi_factor =
match self.with_window(xev.event, |window| window.hidpi_factor()) {
Some(dpi_factor) => dpi_factor,
None => return,
};
let window_id = mkwid(xev.event);
wt.ime
@ -920,10 +882,9 @@ impl<T: 'static> EventProcessor<T> {
.map(|device| device.attachment)
.unwrap_or(2);
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let position =
PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
@ -966,15 +927,11 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!(),
};
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
if self.window_exists(xev.event) {
let id = xev.detail as u64;
let modifiers = self.device_mod_state.modifiers();
let location = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
let location =
PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64);
// Mouse cursor position changes when touch events are received.
// Only the first concurrently active touch ID moves the mouse cursor.
@ -1163,21 +1120,40 @@ impl<T: 'static> EventProcessor<T> {
// Check if the window is on this monitor
let monitor = window.current_monitor();
if monitor.name == new_monitor.name {
callback(Event::WindowEvent {
window_id: mkwid(window_id.0),
event: WindowEvent::HiDpiFactorChanged(
new_monitor.hidpi_factor,
),
});
let (width, height) =
window.inner_size_physical();
let (_, _, flusher) = window.adjust_for_dpi(
prev_monitor.hidpi_factor,
new_monitor.hidpi_factor,
width as f64,
height as f64,
let (new_width, new_height) = window
.adjust_for_dpi(
prev_monitor.hidpi_factor,
new_monitor.hidpi_factor,
width,
height,
);
let window_id = crate::window::WindowId(
crate::platform_impl::platform::WindowId::X(
*window_id,
),
);
flusher.queue();
let mut new_inner_size = Some(
PhysicalSize::new(new_width, new_height),
);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::HiDpiFactorChanged {
hidpi_factor: new_monitor.hidpi_factor,
new_inner_size: &mut new_inner_size,
},
});
if let Some(new_size) = new_inner_size {
let (new_width, new_height) =
new_size.into();
window.set_inner_size_physical(
new_width, new_height,
);
}
}
}
}
@ -1203,7 +1179,7 @@ impl<T: 'static> EventProcessor<T> {
state: ElementState,
callback: &mut F,
) where
F: FnMut(Event<T>),
F: FnMut(Event<'_, T>),
{
let wt = get_xtarget(&self.target);

View file

@ -24,7 +24,7 @@ pub use self::{
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
collections::{HashMap, HashSet},
ffi::CStr,
mem::{self, MaybeUninit},
ops::Deref,
@ -37,6 +37,10 @@ use std::{
use libc::{self, setlocale, LC_CTYPE};
use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, SendError, Sender};
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
@ -51,6 +55,9 @@ use crate::{
window::WindowAttributes,
};
const X_TOKEN: Token = Token(0);
const USER_TOKEN: Token = Token(1);
pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom,
@ -64,18 +71,15 @@ pub struct EventLoopWindowTarget<T> {
}
pub struct EventLoop<T: 'static> {
inner_loop: ::calloop::EventLoop<()>,
_x11_source: ::calloop::Source<::calloop::generic::Generic<::calloop::generic::EventedRawFd>>,
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
event_processor: Rc<RefCell<EventProcessor<T>>>,
user_sender: ::calloop::channel::Sender<T>,
pending_events: Rc<RefCell<VecDeque<Event<T>>>>,
pub(crate) target: Rc<RootELW<T>>,
poll: Poll,
event_processor: EventProcessor<T>,
user_channel: Receiver<T>,
user_sender: Sender<T>,
target: Rc<RootELW<T>>,
}
pub struct EventLoopProxy<T: 'static> {
user_sender: ::calloop::channel::Sender<T>,
user_sender: Sender<T>,
}
impl<T: 'static> Clone for EventLoopProxy<T> {
@ -171,28 +175,27 @@ impl<T: 'static> EventLoop<T> {
_marker: ::std::marker::PhantomData,
});
// A calloop event loop to drive us
let inner_loop = ::calloop::EventLoop::new().unwrap();
let poll = Poll::new().unwrap();
// Handle user events
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = channel();
let (user_sender, user_channel) = ::calloop::channel::channel();
poll.register(
&EventedFd(&get_xtarget(&target).xconn.x11_fd),
X_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
let _user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
poll.register(
&user_channel,
USER_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
// Handle X11 events
let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default();
let processor = EventProcessor {
let event_processor = EventProcessor {
target: target.clone(),
dnd,
devices: Default::default(),
@ -212,38 +215,12 @@ impl<T: 'static> EventLoop<T> {
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
.queue();
processor.init_device(ffi::XIAllDevices);
let processor = Rc::new(RefCell::new(processor));
let event_processor = processor.clone();
// Setup the X11 event source
let mut x11_events =
::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
x11_events.set_interest(::calloop::mio::Ready::readable());
let _x11_source = inner_loop
.handle()
.insert_source(x11_events, {
let pending_events = pending_events.clone();
move |evt, &mut ()| {
if evt.readiness.is_readable() {
let mut processor = processor.borrow_mut();
let mut pending_events = pending_events.borrow_mut();
let mut pending_redraws = pending_redraws.lock().unwrap();
drain_events(&mut processor, &mut pending_events, &mut pending_redraws);
}
}
})
.unwrap();
event_processor.init_device(ffi::XIAllDevices);
let result = EventLoop {
inner_loop,
pending_events,
_x11_source,
_user_source,
poll,
user_channel,
user_sender,
pending_user_events,
event_processor,
target,
};
@ -261,12 +238,16 @@ impl<T: 'static> EventLoop<T> {
&self.target
}
pub(crate) fn x_connection(&self) -> &Arc<XConnection> {
get_xtarget(&self.target).x_connection()
}
pub fn run_return<F>(&mut self, mut callback: F)
where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();
let wt = get_xtarget(&self.target);
let mut events = Events::with_capacity(8);
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
@ -275,22 +256,16 @@ impl<T: 'static> EventLoop<T> {
);
loop {
self.drain_events();
// Process all pending events
self.drain_events(&mut callback, &mut control_flow);
// Empty the event buffer
{
let mut guard = self.pending_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback);
}
}
let wt = get_xtarget(&self.target);
// Empty the user event buffer
{
let mut guard = self.pending_user_events.borrow_mut();
for evt in guard.drain(..) {
while let Ok(event) = self.user_channel.try_recv() {
sticky_exit_callback(
crate::event::Event::UserEvent(evt),
crate::event::Event::UserEvent(event),
&self.target,
&mut control_flow,
&mut callback,
@ -331,7 +306,7 @@ impl<T: 'static> EventLoop<T> {
}
let start = Instant::now();
let (mut cause, deadline, mut timeout);
let (mut cause, deadline, timeout);
match control_flow {
ControlFlow::Exit => break,
@ -362,26 +337,39 @@ impl<T: 'static> EventLoop<T> {
}
}
if self.events_waiting() {
timeout = Some(Duration::from_millis(0));
}
if self.event_processor.poll() {
// If the XConnection already contains buffered events, we don't
// need to wait for data on the socket.
// However, we still need to check for user events.
self.poll
.poll(&mut events, Some(Duration::from_millis(0)))
.unwrap();
events.clear();
self.inner_loop.dispatch(timeout, &mut ()).unwrap();
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
} else {
self.poll.poll(&mut events, timeout).unwrap();
events.clear();
if let Some(deadline) = deadline {
if deadline > Instant::now() {
let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
if wait_cancelled {
cause = StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
requested_resume: deadline,
};
}
}
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
);
}
}
callback(
@ -393,44 +381,42 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
::std::process::exit(0);
}
fn drain_events(&self) {
let mut processor = self.event_processor.borrow_mut();
let mut pending_events = self.pending_events.borrow_mut();
fn drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow)
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let target = &self.target;
let mut xev = MaybeUninit::uninit();
let wt = get_xtarget(&self.target);
let mut pending_redraws = wt.pending_redraws.lock().unwrap();
drain_events(&mut processor, &mut pending_events, &mut pending_redraws);
}
fn events_waiting(&self) -> bool {
!self.pending_events.borrow().is_empty() || self.event_processor.borrow().poll()
}
}
fn drain_events<T: 'static>(
processor: &mut EventProcessor<T>,
pending_events: &mut VecDeque<Event<T>>,
pending_redraws: &mut HashSet<WindowId>,
) {
let mut callback = |event| {
if let Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))) = event {
pending_redraws.insert(wid);
} else {
pending_events.push_back(event);
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |event| {
sticky_exit_callback(
event,
target,
control_flow,
&mut |event, window_target, control_flow| {
if let Event::RedrawRequested(crate::window::WindowId(
super::WindowId::X(wid),
)) = event
{
pending_redraws.insert(wid);
} else {
callback(event, window_target, control_flow);
}
},
);
});
}
};
// process all pending events
let mut xev = MaybeUninit::uninit();
while unsafe { processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() };
processor.process_event(&mut xev, &mut callback);
}
}
@ -452,7 +438,7 @@ impl<T> EventLoopWindowTarget<T> {
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
EventLoopClosed(if let SendError::Disconnected(x) = e {
x
} else {
unreachable!()

View file

@ -15,7 +15,7 @@ use libc;
use parking_lot::Mutex;
use crate::{
dpi::{LogicalPosition, LogicalSize},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform_impl::{
@ -36,7 +36,7 @@ pub struct SharedState {
pub inner_position: Option<(i32, i32)>,
pub inner_position_rel_parent: Option<(i32, i32)>,
pub last_monitor: X11MonitorHandle,
pub dpi_adjusted: Option<(f64, f64)>,
pub dpi_adjusted: Option<(u32, u32)>,
pub fullscreen: Option<Fullscreen>,
// Set when application calls `set_fullscreen` when window is not visible
pub desired_fullscreen: Option<Option<Fullscreen>>,
@ -45,8 +45,8 @@ pub struct SharedState {
// Used to restore video mode after exiting fullscreen
pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>,
pub frame_extents: Option<util::FrameExtentsHeuristic>,
pub min_inner_size: Option<LogicalSize>,
pub max_inner_size: Option<LogicalSize>,
pub min_inner_size: Option<Size>,
pub max_inner_size: Option<Size>,
pub visibility: Visibility,
}
@ -148,8 +148,8 @@ impl UnownedWindow {
// by the user, so we have to manually apply the initial constraints
let mut dimensions: (u32, u32) = window_attrs
.inner_size
.or_else(|| Some((800, 600).into()))
.map(|size| size.to_physical(dpi_factor))
.or_else(|| Some((800, 600).into()))
.map(Into::into)
.unwrap();
if let Some(max) = max_inner_size {
@ -440,16 +440,6 @@ impl UnownedWindow {
.map_err(|x_err| os_error!(OsError::XError(x_err)))
}
fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition {
let dpi = self.hidpi_factor();
LogicalPosition::from_physical((x, y), dpi)
}
fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize {
let dpi = self.hidpi_factor();
LogicalSize::from_physical((width, height), dpi)
}
fn set_pid(&self) -> Option<util::Flusher<'_>> {
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
@ -951,11 +941,11 @@ impl UnownedWindow {
}
#[inline]
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
pub fn outer_position(&self) -> Result<PhysicalPosition, NotSupportedError> {
let extents = (*self.shared_state.lock()).frame_extents.clone();
if let Some(extents) = extents {
let logical = self.inner_position().unwrap();
Ok(extents.inner_pos_to_outer_logical(logical, self.hidpi_factor()))
let (x, y) = self.inner_position_physical();
Ok(extents.inner_pos_to_outer(x, y).into())
} else {
self.update_cached_frame_extents();
self.outer_position()
@ -972,8 +962,8 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
Ok(self.logicalize_coords(self.inner_position_physical()))
pub fn inner_position(&self) -> Result<PhysicalPosition, NotSupportedError> {
Ok(self.inner_position_physical().into())
}
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> {
@ -1002,8 +992,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_outer_position(&self, logical_position: LogicalPosition) {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
pub fn set_outer_position(&self, position: Position) {
let (x, y) = position.to_physical(self.hidpi_factor()).into();
self.set_position_physical(x, y);
}
@ -1017,16 +1007,16 @@ impl UnownedWindow {
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
self.logicalize_size(self.inner_size_physical())
pub fn inner_size(&self) -> PhysicalSize {
self.inner_size_physical().into()
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
pub fn outer_size(&self) -> PhysicalSize {
let extents = self.shared_state.lock().frame_extents.clone();
if let Some(extents) = extents {
let logical = self.inner_size();
extents.inner_size_to_outer_logical(logical, self.hidpi_factor())
let (width, height) = self.inner_size_physical();
extents.inner_size_to_outer(width, height).into()
} else {
self.update_cached_frame_extents();
self.outer_size()
@ -1047,9 +1037,9 @@ impl UnownedWindow {
}
#[inline]
pub fn set_inner_size(&self, logical_size: LogicalSize) {
pub fn set_inner_size(&self, size: Size) {
let dpi_factor = self.hidpi_factor();
let (width, height) = logical_size.to_physical(dpi_factor).into();
let (width, height) = size.to_physical(dpi_factor).into();
self.set_inner_size_physical(width, height);
}
@ -1070,10 +1060,10 @@ impl UnownedWindow {
}
#[inline]
pub fn set_min_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().min_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
self.shared_state.lock().min_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical(self.hidpi_factor()).into());
self.set_min_inner_size_physical(physical_dimensions);
}
@ -1083,10 +1073,10 @@ impl UnownedWindow {
}
#[inline]
pub fn set_max_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().max_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
self.shared_state.lock().max_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical(self.hidpi_factor()).into());
self.set_max_inner_size_physical(physical_dimensions);
}
@ -1094,12 +1084,10 @@ impl UnownedWindow {
&self,
old_dpi_factor: f64,
new_dpi_factor: f64,
width: f64,
height: f64,
) -> (f64, f64, util::Flusher<'_>) {
width: u32,
height: u32,
) -> (u32, u32) {
let scale_factor = new_dpi_factor / old_dpi_factor;
let new_width = width * scale_factor;
let new_height = height * scale_factor;
self.update_normal_hints(|normal_hints| {
let dpi_adjuster = |(width, height): (u32, u32)| -> (u32, u32) {
let new_width = width as f64 * scale_factor;
@ -1116,15 +1104,11 @@ impl UnownedWindow {
normal_hints.set_base_size(base_size);
})
.expect("Failed to update normal hints");
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
self.xwindow,
new_width.round() as c_uint,
new_height.round() as c_uint,
);
}
(new_width, new_height, util::Flusher::new(&self.xconn))
let new_width = (width as f64 * scale_factor).round() as u32;
let new_height = (height as f64 * scale_factor).round() as u32;
(new_width, new_height)
}
pub fn set_resizable(&self, resizable: bool) {
@ -1136,25 +1120,25 @@ impl UnownedWindow {
return;
}
let (logical_min, logical_max) = if resizable {
let (min_size, max_size) = if resizable {
let shared_state_lock = self.shared_state.lock();
(
shared_state_lock.min_inner_size,
shared_state_lock.max_inner_size,
)
} else {
let window_size = Some(self.inner_size());
let window_size = Some(Size::from(self.inner_size()));
(window_size.clone(), window_size)
};
self.set_maximizable_inner(resizable).queue();
let dpi_factor = self.hidpi_factor();
let min_inner_size = logical_min
.map(|logical_size| logical_size.to_physical(dpi_factor))
let min_inner_size = min_size
.map(|size| size.to_physical(dpi_factor))
.map(Into::into);
let max_inner_size = logical_max
.map(|logical_size| logical_size.to_physical(dpi_factor))
let max_inner_size = max_size
.map(|size| size.to_physical(dpi_factor))
.map(Into::into);
self.update_normal_hints(|normal_hints| {
normal_hints.set_min_size(min_inner_size);
@ -1289,11 +1273,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
let (x, y) = position.to_physical(self.hidpi_factor()).into();
self.set_cursor_position_physical(x, y)
}
@ -1305,8 +1286,8 @@ impl UnownedWindow {
}
#[inline]
pub fn set_ime_position(&self, logical_spot: LogicalPosition) {
let (x, y) = logical_spot.to_physical(self.hidpi_factor()).into();
pub fn set_ime_position(&self, spot: Position) {
let (x, y) = spot.to_physical(self.hidpi_factor()).into();
self.set_ime_position_physical(x, y);
}