DPI for everyone (#548)

This commit is contained in:
Francesca Frangipane 2018-06-14 19:42:18 -04:00 committed by GitHub
parent f083dae328
commit 1b74822cfc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 3096 additions and 1663 deletions

View file

@ -9,13 +9,7 @@ mod dnd;
mod ime;
pub mod util;
pub use self::monitor::{
MonitorId,
get_available_monitors,
get_monitor_for_window,
get_primary_monitor,
invalidate_cached_monitor_list,
};
pub use self::monitor::MonitorId;
pub use self::window::UnownedWindow;
pub use self::xdisplay::{XConnection, XNotSupported, XError};
@ -29,7 +23,6 @@ use std::sync::{Arc, mpsc, Weak};
use std::sync::atomic::{self, AtomicBool};
use libc::{self, setlocale, LC_CTYPE};
use parking_lot::Mutex;
use {
ControlFlow,
@ -38,6 +31,8 @@ use {
Event,
EventsLoopClosed,
KeyboardInput,
LogicalPosition,
LogicalSize,
WindowAttributes,
WindowEvent,
};
@ -92,7 +87,7 @@ impl EventsLoop {
result.expect("Failed to set input method destruction callback")
});
let randr_event_offset = monitor::select_input(&xconn, root)
let randr_event_offset = xconn.select_xrandr_input(root)
.expect("Failed to query XRandR extension");
let xi2ext = unsafe {
@ -394,6 +389,13 @@ impl EventsLoop {
}
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| {
@ -406,9 +408,11 @@ impl EventsLoop {
// that has a position relative to the parent window.
let is_synthetic = xev.send_event == ffi::True;
// These are both in physical space.
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 monitor = window.get_current_monitor(); // This must be done *before* locking!
let mut shared_state_lock = window.shared_state.lock();
let (resized, moved) = {
@ -431,14 +435,33 @@ impl EventsLoop {
(resized, moved)
};
let capacity = resized as usize + moved as usize;
let mut events = Vec::with_capacity(capacity);
if resized {
events.push(WindowEvent::Resized(new_inner_size.0, new_inner_size.1));
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
// doesn't need this, but Xfwm does.
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 {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
}
}
if moved || shared_state_lock.position.is_none() {
let mut events = Events::default();
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
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.frame_extents
.as_ref()
@ -451,19 +474,59 @@ impl EventsLoop {
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
shared_state_lock.position = Some(outer);
if moved {
events.push(WindowEvent::Moved(outer.0, outer.1));
let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
events.moved = Some(WindowEvent::Moved(logical_position));
}
outer
} else {
shared_state_lock.position.unwrap()
};
// If we don't use the existing adjusted value when available, then the user can screw up the
// 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));
let last_hidpi_factor = if shared_state_lock.is_new_window {
shared_state_lock.is_new_window = false;
1.0
} else {
shared_state_lock.last_monitor
.as_ref()
.map(|last_monitor| last_monitor.hidpi_factor)
.unwrap_or(1.0)
};
let new_hidpi_factor = {
let window_rect = util::Rect::new(new_outer_position, new_inner_size);
let monitor = self.xconn.get_monitor_for_window(Some(window_rect));
let new_hidpi_factor = monitor.hidpi_factor;
shared_state_lock.last_monitor = Some(monitor);
new_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(
last_hidpi_factor,
new_hidpi_factor,
width,
height,
);
flusher.queue();
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
}
events
});
if let Some(events) = events {
for event in events {
callback(Event::WindowEvent {
window_id: mkwid(xwindow),
event,
});
let window_id = mkwid(xwindow);
if let Some(event) = events.resized {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.moved {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
}
}
@ -694,14 +757,25 @@ impl EventsLoop {
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
});
if cursor_moved == Some(true) {
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id,
position: new_cursor_pos,
modifiers,
},
let dpi_factor = self.with_window(xev.event, |window| {
window.get_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;
}
} else if cursor_moved.is_none() {
return;
}
@ -782,19 +856,29 @@ impl EventsLoop {
event: CursorEntered { device_id },
});
let new_cursor_pos = (xev.event_x, xev.event_y);
// 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
// relying on Xkb for modifier values.
let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid)
.expect("Failed to query pointer device").get_modifier_state();
callback(Event::WindowEvent { window_id, event: CursorMoved {
device_id,
position: new_cursor_pos,
modifiers,
}})
let dpi_factor = self.with_window(xev.event, |window| {
window.get_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,
},
});
}
}
ffi::XI_Leave => {
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
@ -812,7 +896,12 @@ impl EventsLoop {
ffi::XI_FocusIn => {
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
if !self.window_exists(xev.event) { return; }
let dpi_factor = match self.with_window(xev.event, |window| {
window.get_hidpi_factor()
}) {
Some(dpi_factor) => dpi_factor,
None => return,
};
let window_id = mkwid(xev.event);
self.ime
@ -830,11 +919,15 @@ impl EventsLoop {
.map(|device| device.attachment)
.unwrap_or(2);
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: mkdid(pointer_id),
position: (xev.event_x, xev.event_y),
position,
modifiers: ModifiersState::from(xev.mods),
}
});
@ -861,15 +954,24 @@ impl EventsLoop {
ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!()
};
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Touch(Touch {
device_id: mkdid(xev.deviceid),
phase,
location: (xev.event_x, xev.event_y),
id: xev.detail as u64,
},
)})
let dpi_factor = self.with_window(xev.event, |window| {
window.get_hidpi_factor()
});
if let Some(dpi_factor) = dpi_factor {
let location = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Touch(Touch {
device_id: mkdid(xev.deviceid),
phase,
location,
id: xev.detail as u64,
}),
})
}
}
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
@ -986,7 +1088,44 @@ impl EventsLoop {
_ => {
if event_type == self.randr_event_offset {
// In the future, it would be quite easy to emit monitor hotplug events.
monitor::invalidate_cached_monitor_list();
let prev_list = monitor::invalidate_cached_monitor_list();
if let Some(prev_list) = prev_list {
let new_list = self.xconn.get_available_monitors();
for new_monitor in new_list {
prev_list
.iter()
.find(|prev_monitor| prev_monitor.name == new_monitor.name)
.map(|prev_monitor| {
if new_monitor.hidpi_factor != prev_monitor.hidpi_factor {
for (window_id, window) in self.windows.borrow().iter() {
if let Some(window) = window.upgrade() {
// Check if the window is on this monitor
let monitor = window.get_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) = match window.get_inner_size_physical() {
Some(result) => result,
None => continue,
};
let (_, _, flusher) = window.adjust_for_dpi(
prev_monitor.hidpi_factor,
new_monitor.hidpi_factor,
width as f64,
height as f64,
);
flusher.queue();
}
}
}
}
});
}
}
}
},
}
@ -1110,16 +1249,13 @@ pub struct WindowId(ffi::Window);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId(c_int);
pub struct Window {
pub window: Arc<UnownedWindow>,
ime_sender: Mutex<ImeSender>,
}
pub struct Window(Arc<UnownedWindow>);
impl Deref for Window {
type Target = UnownedWindow;
#[inline]
fn deref(&self) -> &UnownedWindow {
&*self.window
&*self.0
}
}
@ -1130,35 +1266,19 @@ impl Window {
pl_attribs: PlatformSpecificWindowBuilderAttributes
) -> Result<Self, CreationError> {
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
event_loop.windows
.borrow_mut()
.insert(window.id(), Arc::downgrade(&window));
event_loop.ime
.borrow_mut()
.create_context(window.id().0)
.expect("Failed to create input context");
Ok(Window {
window,
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
})
}
#[inline]
pub fn send_xim_spot(&self, x: i16, y: i16) {
let _ = self.ime_sender
.lock()
.send((self.window.id().0, x, y));
Ok(Window(window))
}
}
impl Drop for Window {
fn drop(&mut self) {
let xconn = &self.window.xconn;
let window = self.deref();
let xconn = &window.xconn;
unsafe {
(xconn.xlib.XDestroyWindow)(xconn.display, self.window.id().0);
(xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
let _ = xconn.check_errors();
}