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

@ -8,15 +8,16 @@ use std::sync::Arc;
use sctk::reexports::client::ConnectError;
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
use libc;
use {
CreationError,
CursorState,
EventsLoopClosed,
Icon,
LogicalPosition,
LogicalSize,
MouseCursor,
PhysicalPosition,
PhysicalSize,
ControlFlow,
WindowAttributes,
};
@ -57,19 +58,19 @@ thread_local!(
pub enum Window {
X(x11::Window),
Wayland(wayland::Window)
Wayland(wayland::Window),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum WindowId {
X(x11::WindowId),
Wayland(wayland::WindowId)
Wayland(wayland::WindowId),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum DeviceId {
X(x11::DeviceId),
Wayland(wayland::DeviceId)
Wayland(wayland::DeviceId),
}
#[derive(Debug, Clone)]
@ -96,7 +97,7 @@ impl MonitorId {
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
match self {
&MonitorId::X(ref m) => m.get_dimensions(),
&MonitorId::Wayland(ref m) => m.get_dimensions(),
@ -104,7 +105,7 @@ impl MonitorId {
}
#[inline]
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
match self {
&MonitorId::X(ref m) => m.get_position(),
&MonitorId::Wayland(ref m) => m.get_position(),
@ -112,10 +113,10 @@ impl MonitorId {
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
match self {
&MonitorId::X(ref m) => m.get_hidpi_factor(),
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
&MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64,
}
}
}
@ -141,7 +142,7 @@ impl Window {
pub fn id(&self) -> WindowId {
match self {
&Window::X(ref w) => WindowId::X(w.id()),
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
&Window::Wayland(ref w) => WindowId::Wayland(w.id()),
}
}
@ -149,7 +150,7 @@ impl Window {
pub fn set_title(&self, title: &str) {
match self {
&Window::X(ref w) => w.set_title(title),
&Window::Wayland(ref w) => w.set_title(title)
&Window::Wayland(ref w) => w.set_title(title),
}
}
@ -157,7 +158,7 @@ impl Window {
pub fn show(&self) {
match self {
&Window::X(ref w) => w.show(),
&Window::Wayland(ref w) => w.show()
&Window::Wayland(ref w) => w.show(),
}
}
@ -165,20 +166,20 @@ impl Window {
pub fn hide(&self) {
match self {
&Window::X(ref w) => w.hide(),
&Window::Wayland(ref w) => w.hide()
&Window::Wayland(ref w) => w.hide(),
}
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
match self {
&Window::X(ref w) => w.get_position(),
&Window::Wayland(ref w) => w.get_position()
&Window::Wayland(ref w) => w.get_position(),
}
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
match self {
&Window::X(ref m) => m.get_inner_position(),
&Window::Wayland(ref m) => m.get_inner_position(),
@ -186,53 +187,53 @@ impl Window {
}
#[inline]
pub fn set_position(&self, x: i32, y: i32) {
pub fn set_position(&self, position: LogicalPosition) {
match self {
&Window::X(ref w) => w.set_position(x, y),
&Window::Wayland(ref w) => w.set_position(x, y)
&Window::X(ref w) => w.set_position(position),
&Window::Wayland(ref w) => w.set_position(position),
}
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn get_inner_size(&self) -> Option<LogicalSize> {
match self {
&Window::X(ref w) => w.get_inner_size(),
&Window::Wayland(ref w) => w.get_inner_size()
&Window::Wayland(ref w) => w.get_inner_size(),
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
match self {
&Window::X(ref w) => w.get_outer_size(),
&Window::Wayland(ref w) => w.get_outer_size()
&Window::Wayland(ref w) => w.get_outer_size(),
}
}
#[inline]
pub fn set_inner_size(&self, x: u32, y: u32) {
pub fn set_inner_size(&self, size: LogicalSize) {
match self {
&Window::X(ref w) => w.set_inner_size(x, y),
&Window::Wayland(ref w) => w.set_inner_size(x, y)
&Window::X(ref w) => w.set_inner_size(size),
&Window::Wayland(ref w) => w.set_inner_size(size),
}
}
#[inline]
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
match self {
&Window::X(ref w) => w.set_min_dimensions(dimensions),
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions),
}
}
#[inline]
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
match self {
&Window::X(ref w) => w.set_max_dimensions(dimensions),
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions),
}
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
match self {
@ -258,34 +259,18 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
match self {
&Window::X(ref w) => w.hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor()
&Window::X(ref w) => w.get_hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
}
}
#[inline]
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), ()> {
match self {
&Window::X(ref w) => w.set_cursor_position(x, y),
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
}
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
match self {
&Window::X(ref w) => w.platform_display(),
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
}
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
match self {
&Window::X(ref w) => w.platform_window(),
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
&Window::X(ref w) => w.set_cursor_position(position),
&Window::Wayland(ref w) => w.set_cursor_position(position),
}
}
@ -330,9 +315,9 @@ impl Window {
}
#[inline]
pub fn set_ime_spot(&self, x: i32, y: i32) {
pub fn set_ime_spot(&self, position: LogicalPosition) {
match self {
&Window::X(ref w) => w.send_xim_spot(x as i16, y as i16),
&Window::X(ref w) => w.set_ime_spot(position),
&Window::Wayland(_) => (),
}
}
@ -447,14 +432,17 @@ r#"Failed to initialize any backend!
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
match *self {
EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors()
.into_iter()
.map(MonitorId::Wayland)
.collect(),
EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection())
.into_iter()
.map(MonitorId::X)
.collect(),
EventsLoop::Wayland(ref evlp) => evlp
.get_available_monitors()
.into_iter()
.map(MonitorId::Wayland)
.collect(),
EventsLoop::X(ref evlp) => evlp
.x_connection()
.get_available_monitors()
.into_iter()
.map(MonitorId::X)
.collect(),
}
}
@ -462,7 +450,7 @@ r#"Failed to initialize any backend!
pub fn get_primary_monitor(&self) -> MonitorId {
match *self {
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())),
EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()),
}
}

View file

@ -4,7 +4,7 @@ use std::fmt;
use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use {ControlFlow, EventsLoopClosed};
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
use super::WindowId;
use super::window::WindowStore;
@ -248,16 +248,21 @@ impl EventsLoop {
}
// process pending resize/refresh
self.store.lock().unwrap().for_each(
|newsize, refresh, frame_refresh, closed, wid, frame| {
|newsize, size, new_dpi, refresh, frame_refresh, closed, wid, frame| {
if let Some(frame) = frame {
if let Some((w, h)) = newsize {
frame.resize(w as u32, h as u32);
frame.resize(w, h);
frame.refresh();
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
let logical_size = ::LogicalSize::new(w as f64, h as f64);
sink.send_event(::WindowEvent::Resized(logical_size), wid);
*size = (w, h);
} else if frame_refresh {
frame.refresh();
}
}
if let Some(dpi) = new_dpi {
sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
}
if refresh {
sink.send_event(::WindowEvent::Refresh, wid);
}
@ -434,9 +439,9 @@ impl fmt::Debug for MonitorId {
struct MonitorId {
name: Option<String>,
native_identifier: u32,
dimensions: (u32, u32),
position: (i32, i32),
hidpi_factor: f32,
dimensions: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: i32,
}
let monitor_id_proxy = MonitorId {
@ -463,7 +468,7 @@ impl MonitorId {
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
}
pub fn get_dimensions(&self) -> (u32, u32) {
pub fn get_dimensions(&self) -> PhysicalSize {
match self.mgr.with_info(&self.proxy, |_, info| {
info.modes
.iter()
@ -472,19 +477,20 @@ impl MonitorId {
}) {
Some(Some((w, h))) => (w as u32, h as u32),
_ => (0, 0),
}
}.into()
}
pub fn get_position(&self) -> (i32, i32) {
pub fn get_position(&self) -> PhysicalPosition {
self.mgr
.with_info(&self.proxy, |_, info| info.location)
.unwrap_or((0, 0))
.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> i32 {
self.mgr
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
.unwrap_or(1.0)
.with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1)
}
}

View file

@ -42,7 +42,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
@ -71,7 +71,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
@ -117,7 +117,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
@ -158,7 +158,7 @@ pub fn implement_pointer(
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),

View file

@ -34,7 +34,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Started,
location: (x, y),
location: (x, y).into(),
id: id as u64,
}),
wid,
@ -54,7 +54,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Ended,
location: pt.location,
location: pt.location.into(),
id: id as u64,
}),
pt.wid,
@ -69,7 +69,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Moved,
location: (x, y),
location: (x, y).into(),
id: id as u64,
}),
pt.wid,
@ -82,7 +82,7 @@ pub(crate) fn implement_touch(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Cancelled,
location: pt.location,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,

View file

@ -1,12 +1,12 @@
use std::sync::{Arc, Mutex, Weak};
use {CreationError, CursorState, MouseCursor, WindowAttributes};
use {CreationError, CursorState, MouseCursor, WindowAttributes, LogicalPosition, LogicalSize};
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
use sctk::reexports::client::{Display, Proxy};
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
@ -15,7 +15,7 @@ use super::{make_wid, EventsLoop, MonitorId, WindowId};
pub struct Window {
surface: Proxy<wl_surface::WlSurface>,
frame: Arc<Mutex<SWindow<BasicFrame>>>,
monitors: Arc<Mutex<Vec<MonitorId>>>,
monitors: Arc<Mutex<MonitorList>>,
size: Arc<Mutex<(u32, u32)>>,
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
display: Arc<Display>,
@ -24,23 +24,42 @@ pub struct Window {
impl Window {
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
// TODO: Update for new DPI API
//let (width, height) = attributes.dimensions.unwrap_or((800, 600));
let (width, height) = (64, 64);
// Create the window
let size = Arc::new(Mutex::new((width, height)));
// monitor tracking
let monitor_list = Arc::new(Mutex::new(Vec::new()));
let monitor_list = Arc::new(Mutex::new(MonitorList::new()));
let surface = evlp.env.compositor.create_surface().unwrap().implement({
let list = monitor_list.clone();
let omgr = evlp.env.outputs.clone();
move |event, _| match event {
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
proxy: output,
mgr: omgr.clone(),
}),
let window_store = evlp.store.clone();
move |event, surface: Proxy<wl_surface::WlSurface>| match event {
wl_surface::Event::Enter { output } => {
let dpi_change = list.lock().unwrap().add_output(MonitorId {
proxy: output,
mgr: omgr.clone(),
});
if let Some(dpi) = dpi_change {
if surface.version() >= 3 {
// without version 3 we can't be dpi aware
window_store.lock().unwrap().dpi_change(&surface, dpi);
surface.set_buffer_scale(dpi);
}
}
},
wl_surface::Event::Leave { output } => {
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
let dpi_change = list.lock().unwrap().del_output(&output);
if let Some(dpi) = dpi_change {
if surface.version() >= 3 {
// without version 3 we can't be dpi aware
window_store.lock().unwrap().dpi_change(&surface, dpi);
surface.set_buffer_scale(dpi);
}
}
}
}
});
@ -59,7 +78,7 @@ impl Window {
let mut store = window_store.lock().unwrap();
for window in &mut store.windows {
if window.surface.equals(&my_surface) {
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
window.newsize = new_size;
window.need_refresh = true;
*(window.need_frame_refresh.lock().unwrap()) = true;
return;
@ -107,8 +126,9 @@ impl Window {
frame.set_decorate(attributes.decorations);
// min-max dimensions
frame.set_min_size(attributes.min_dimensions);
frame.set_max_size(attributes.max_dimensions);
// TODO: Update for new DPI API
//frame.set_min_size(attributes.min_dimensions);
//frame.set_max_size(attributes.max_dimensions);
let kill_switch = Arc::new(Mutex::new(false));
let need_frame_refresh = Arc::new(Mutex::new(true));
@ -117,11 +137,14 @@ impl Window {
evlp.store.lock().unwrap().windows.push(InternalWindow {
closed: false,
newsize: None,
size: size.clone(),
need_refresh: false,
need_frame_refresh: need_frame_refresh.clone(),
surface: surface.clone(),
kill_switch: kill_switch.clone(),
frame: Arc::downgrade(&frame),
current_dpi: 1,
new_dpi: None,
});
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
@ -156,48 +179,49 @@ impl Window {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub fn get_position(&self) -> Option<LogicalPosition> {
// Not possible with wayland
None
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
// Not possible with wayland
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
pub fn set_position(&self, _pos: LogicalPosition) {
// Not possible with wayland
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
Some(self.size.lock().unwrap().clone())
pub fn get_inner_size(&self) -> Option<LogicalSize> {
Some(self.size.lock().unwrap().clone().into())
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
pub fn get_outer_size(&self) -> Option<LogicalSize> {
let (w, h) = self.size.lock().unwrap().clone();
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
Some((w as u32, h as u32))
Some((w, h).into())
}
#[inline]
// NOTE: This will only resize the borders, the contents must be updated by the user
pub fn set_inner_size(&self, x: u32, y: u32) {
self.frame.lock().unwrap().resize(x, y);
*(self.size.lock().unwrap()) = (x, y);
pub fn set_inner_size(&self, size: LogicalSize) {
let (w, h) = size.into();
self.frame.lock().unwrap().resize(w, h);
*(self.size.lock().unwrap()) = (w, h);
}
#[inline]
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
self.frame.lock().unwrap().set_min_size(dimensions);
pub fn set_min_dimensions(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into));
}
#[inline]
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
self.frame.lock().unwrap().set_max_size(dimensions);
pub fn set_max_dimensions(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into));
}
#[inline]
@ -222,14 +246,8 @@ impl Window {
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
let mut factor: f32 = 1.0;
let guard = self.monitors.lock().unwrap();
for monitor_id in guard.iter() {
let hidpif = monitor_id.get_hidpi_factor();
factor = factor.max(hidpif);
}
factor
pub fn hidpi_factor(&self) -> i32 {
self.monitors.lock().unwrap().compute_hidpi_factor()
}
pub fn set_decorations(&self, decorate: bool) {
@ -260,7 +278,7 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
pub fn set_cursor_position(&self, _pos: LogicalPosition) -> Result<(), ()> {
// TODO: not yet possible on wayland
Err(())
}
@ -277,7 +295,7 @@ impl Window {
// we don't know how much each monitor sees us so...
// just return the most recent one ?
let guard = self.monitors.lock().unwrap();
guard.last().unwrap().clone()
guard.monitors.last().unwrap().clone()
}
}
@ -294,12 +312,15 @@ impl Drop for Window {
struct InternalWindow {
surface: Proxy<wl_surface::WlSurface>,
newsize: Option<(i32, i32)>,
newsize: Option<(u32, u32)>,
size: Arc<Mutex<(u32, u32)>>,
need_refresh: bool,
need_frame_refresh: Arc<Mutex<bool>>,
closed: bool,
kill_switch: Arc<Mutex<bool>>,
frame: Weak<Mutex<SWindow<BasicFrame>>>,
current_dpi: i32,
new_dpi: Option<i32>
}
pub struct WindowStore {
@ -345,24 +366,84 @@ impl WindowStore {
}
}
fn dpi_change(&mut self, surface: &Proxy<wl_surface::WlSurface>, new: i32) {
for window in &mut self.windows {
if surface.equals(&window.surface) {
window.new_dpi = Some(new);
}
}
}
pub fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
{
for window in &mut self.windows {
let opt_arc = window.frame.upgrade();
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
f(
window.newsize.take(),
&mut *(window.size.lock().unwrap()),
window.new_dpi,
window.need_refresh,
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
window.closed,
make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m),
);
if let Some(dpi) = window.new_dpi.take() {
window.current_dpi = dpi;
}
window.need_refresh = false;
// avoid re-spamming the event
window.closed = false;
}
}
}
/*
* Monitor list with some covenience method to compute DPI
*/
struct MonitorList {
monitors: Vec<MonitorId>
}
impl MonitorList {
fn new() -> MonitorList {
MonitorList {
monitors: Vec::new()
}
}
fn compute_hidpi_factor(&self) -> i32 {
let mut factor = 1;
for monitor_id in &self.monitors {
let monitor_dpi = monitor_id.get_hidpi_factor();
if monitor_dpi > factor { factor = monitor_dpi; }
}
factor
}
fn add_output(&mut self, monitor: MonitorId) -> Option<i32> {
let old_dpi = self.compute_hidpi_factor();
let monitor_dpi = monitor.get_hidpi_factor();
self.monitors.push(monitor);
if monitor_dpi > old_dpi {
Some(monitor_dpi)
} else {
None
}
}
fn del_output(&mut self, output: &Proxy<wl_output::WlOutput>) -> Option<i32> {
let old_dpi = self.compute_hidpi_factor();
self.monitors.retain(|m| !m.proxy.equals(output));
let new_dpi = self.compute_hidpi_factor();
if new_dpi != old_dpi {
Some(new_dpi)
} else {
None
}
}
}

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();
}

View file

@ -1,8 +1,9 @@
use std::os::raw::*;
use std::sync::Arc;
use parking_lot::Mutex;
use {PhysicalPosition, PhysicalSize};
use super::{util, XConnection, XError};
use super::ffi::{
RRCrtcChangeNotifyMask,
RROutputPropertyNotifyMask,
@ -11,7 +12,6 @@ use super::ffi::{
Window,
XRRScreenResources,
};
use super::{util, XConnection, XError};
// Used to test XRandR < 1.5 code path. This should always be committed as false.
const FORCE_RANDR_COMPAT: bool = false;
@ -45,7 +45,7 @@ pub struct MonitorId {
/// The actual id
id: u32,
/// The name of the monitor
name: String,
pub(crate) name: String,
/// The size of the monitor
dimensions: (u32, u32),
/// The position of the monitor in the X screen
@ -53,14 +53,14 @@ pub struct MonitorId {
/// If the monitor is the primary one
primary: bool,
/// The DPI scale factor
pub(crate) hidpi_factor: f32,
pub(crate) hidpi_factor: f64,
/// Used to determine which windows are on this monitor
pub(crate) rect: util::Rect,
}
impl MonitorId {
fn from_repr(
xconn: &Arc<XConnection>,
xconn: &XConnection,
resources: *mut XRRScreenResources,
id: u32,
repr: util::MonitorRepr,
@ -89,182 +89,181 @@ impl MonitorId {
self.id as u32
}
pub fn get_dimensions(&self) -> (u32, u32) {
self.dimensions
pub fn get_dimensions(&self) -> PhysicalSize {
self.dimensions.into()
}
pub fn get_position(&self) -> (i32, i32) {
self.position
pub fn get_position(&self) -> PhysicalPosition {
self.position.into()
}
#[inline]
pub fn get_hidpi_factor(&self) -> f32 {
pub fn get_hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
}
pub fn get_monitor_for_window(
xconn: &Arc<XConnection>,
window_rect: Option<util::Rect>,
) -> MonitorId {
let monitors = get_available_monitors(xconn);
let default = monitors
.get(0)
.expect("[winit] Failed to find any monitors using XRandR.");
impl XConnection {
pub fn get_monitor_for_window(&self, window_rect: Option<util::Rect>) -> MonitorId {
let monitors = self.get_available_monitors();
let default = monitors
.get(0)
.expect("[winit] Failed to find any monitors using XRandR.");
let window_rect = match window_rect {
Some(rect) => rect,
None => return default.to_owned(),
};
let window_rect = match window_rect {
Some(rect) => rect,
None => return default.to_owned(),
};
let mut largest_overlap = 0;
let mut matched_monitor = default;
for monitor in &monitors {
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
if overlapping_area > largest_overlap {
largest_overlap = overlapping_area;
matched_monitor = &monitor;
let mut largest_overlap = 0;
let mut matched_monitor = default;
for monitor in &monitors {
let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
if overlapping_area > largest_overlap {
largest_overlap = overlapping_area;
matched_monitor = &monitor;
}
}
matched_monitor.to_owned()
}
matched_monitor.to_owned()
}
fn query_monitor_list(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
unsafe {
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
// Upon failure, `resources` will be null.
let resources = (xconn.xrandr.XRRGetScreenResources)(xconn.display, root);
if resources.is_null() {
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
}
let mut available;
let mut has_primary = false;
if xconn.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
// videowalls.
let xrandr_1_5 = xconn.xrandr_1_5.as_ref().unwrap();
let mut monitor_count = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(xconn.display, root, 1, &mut monitor_count);
assert!(monitor_count >= 0);
available = Vec::with_capacity(monitor_count as usize);
for monitor_index in 0..monitor_count {
let monitor = monitors.offset(monitor_index as isize);
let is_primary = (*monitor).primary != 0;
has_primary |= is_primary;
available.push(MonitorId::from_repr(
xconn,
resources,
monitor_index as u32,
monitor.into(),
is_primary,
));
fn query_monitor_list(&self) -> Vec<MonitorId> {
unsafe {
let root = (self.xlib.XDefaultRootWindow)(self.display);
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
// Upon failure, `resources` will be null.
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root);
if resources.is_null() {
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
}
(xrandr_1_5.XRRFreeMonitors)(monitors);
} else {
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
// videowall setups will also show monitors that aren't in the logical groups the user
// cares about.
let primary = (xconn.xrandr.XRRGetOutputPrimary)(xconn.display, root);
available = Vec::with_capacity((*resources).ncrtc as usize);
for crtc_index in 0..(*resources).ncrtc {
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
let crtc = (xconn.xrandr.XRRGetCrtcInfo)(xconn.display, resources, crtc_id);
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
if is_active {
let crtc = util::MonitorRepr::from(crtc);
let is_primary = crtc.get_output() == primary;
let mut available;
let mut has_primary = false;
if self.xrandr_1_5.is_some() && version_is_at_least(1, 5) && !FORCE_RANDR_COMPAT {
// We're in XRandR >= 1.5, enumerate monitors. This supports things like MST and
// videowalls.
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
let mut monitor_count = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
assert!(monitor_count >= 0);
available = Vec::with_capacity(monitor_count as usize);
for monitor_index in 0..monitor_count {
let monitor = monitors.offset(monitor_index as isize);
let is_primary = (*monitor).primary != 0;
has_primary |= is_primary;
available.push(MonitorId::from_repr(
xconn,
self,
resources,
crtc_id as u32,
crtc,
monitor_index as u32,
monitor.into(),
is_primary,
));
}
(xconn.xrandr.XRRFreeCrtcInfo)(crtc);
(xrandr_1_5.XRRFreeMonitors)(monitors);
} else {
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work except MST and
// videowall setups will also show monitors that aren't in the logical groups the user
// cares about.
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
available = Vec::with_capacity((*resources).ncrtc as usize);
for crtc_index in 0..(*resources).ncrtc {
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
if is_active {
let crtc = util::MonitorRepr::from(crtc);
let is_primary = crtc.get_output() == primary;
has_primary |= is_primary;
available.push(MonitorId::from_repr(
self,
resources,
crtc_id as u32,
crtc,
is_primary,
));
}
(self.xrandr.XRRFreeCrtcInfo)(crtc);
}
}
}
// If no monitors were detected as being primary, we just pick one ourselves!
if !has_primary {
if let Some(ref mut fallback) = available.first_mut() {
// Setting this here will come in handy if we ever add an `is_primary` method.
fallback.primary = true;
// If no monitors were detected as being primary, we just pick one ourselves!
if !has_primary {
if let Some(ref mut fallback) = available.first_mut() {
// Setting this here will come in handy if we ever add an `is_primary` method.
fallback.primary = true;
}
}
}
(xconn.xrandr.XRRFreeScreenResources)(resources);
available
}
}
pub fn get_available_monitors(xconn: &Arc<XConnection>) -> Vec<MonitorId> {
let mut monitors_lock = MONITORS.lock();
(*monitors_lock)
.as_ref()
.cloned()
.or_else(|| {
let monitors = Some(query_monitor_list(xconn));
if !DISABLE_MONITOR_LIST_CACHING {
(*monitors_lock) = monitors.clone();
}
monitors
})
.unwrap()
}
#[inline]
pub fn get_primary_monitor(xconn: &Arc<XConnection>) -> MonitorId {
get_available_monitors(xconn)
.into_iter()
.find(|monitor| monitor.primary)
.expect("[winit] Failed to find any monitors using XRandR.")
}
pub fn select_input(xconn: &Arc<XConnection>, root: Window) -> Result<c_int, XError> {
{
let mut version_lock = XRANDR_VERSION.lock();
if version_lock.is_none() {
let mut major = 0;
let mut minor = 0;
let has_extension = unsafe {
(xconn.xrandr.XRRQueryVersion)(
xconn.display,
&mut major,
&mut minor,
)
};
if has_extension != True {
panic!("[winit] XRandR extension not available.");
}
*version_lock = Some((major, minor));
(self.xrandr.XRRFreeScreenResources)(resources);
available
}
}
let mut event_offset = 0;
let mut error_offset = 0;
let status = unsafe {
(xconn.xrandr.XRRQueryExtension)(
xconn.display,
&mut event_offset,
&mut error_offset,
)
};
if status != True {
xconn.check_errors()?;
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
pub fn get_available_monitors(&self) -> Vec<MonitorId> {
let mut monitors_lock = MONITORS.lock();
(*monitors_lock)
.as_ref()
.cloned()
.or_else(|| {
let monitors = Some(self.query_monitor_list());
if !DISABLE_MONITOR_LIST_CACHING {
(*monitors_lock) = monitors.clone();
}
monitors
})
.unwrap()
}
let mask = RRCrtcChangeNotifyMask
| RROutputPropertyNotifyMask
| RRScreenChangeNotifyMask;
unsafe { (xconn.xrandr.XRRSelectInput)(xconn.display, root, mask) };
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
self.get_available_monitors()
.into_iter()
.find(|monitor| monitor.primary)
.expect("[winit] Failed to find any monitors using XRandR.")
}
Ok(event_offset)
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
{
let mut version_lock = XRANDR_VERSION.lock();
if version_lock.is_none() {
let mut major = 0;
let mut minor = 0;
let has_extension = unsafe {
(self.xrandr.XRRQueryVersion)(
self.display,
&mut major,
&mut minor,
)
};
if has_extension != True {
panic!("[winit] XRandR extension not available.");
}
*version_lock = Some((major, minor));
}
}
let mut event_offset = 0;
let mut error_offset = 0;
let status = unsafe {
(self.xrandr.XRRQueryExtension)(
self.display,
&mut event_offset,
&mut error_offset,
)
};
if status != True {
self.check_errors()?;
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
}
let mask = RRCrtcChangeNotifyMask
| RROutputPropertyNotifyMask
| RRScreenChangeNotifyMask;
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
Ok(event_offset)
}
}

View file

@ -1,6 +1,7 @@
use std::cmp;
use super::*;
use {LogicalPosition, LogicalSize};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Rect {
@ -78,6 +79,24 @@ impl FrameExtents {
pub fn from_border(border: c_ulong) -> Self {
Self::new(border, border, border, border)
}
pub fn as_logical(&self, factor: f64) -> LogicalFrameExtents {
let logicalize = |value: c_ulong| value as f64 / factor;
LogicalFrameExtents {
left: logicalize(self.left),
right: logicalize(self.right),
top: logicalize(self.top),
bottom: logicalize(self.bottom),
}
}
}
#[derive(Debug, Clone)]
pub struct LogicalFrameExtents {
pub left: f64,
pub right: f64,
pub top: f64,
pub bottom: f64,
}
#[derive(Debug, Clone, PartialEq)]
@ -103,6 +122,16 @@ impl FrameExtentsHeuristic {
}
}
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
let frame_extents = self.frame_extents.as_logical(factor);
logical.x -= frame_extents.left;
logical.y -= frame_extents.top;
}
logical
}
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
(
width.saturating_add(
@ -113,6 +142,13 @@ impl FrameExtentsHeuristic {
),
)
}
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize {
let frame_extents = self.frame_extents.as_logical(factor);
logical.width += frame_extents.left + frame_extents.right;
logical.height += frame_extents.top + frame_extents.bottom;
logical
}
}
impl XConnection {

View file

@ -68,6 +68,99 @@ impl WindowType {
}
}
pub struct NormalHints<'a> {
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
}
impl<'a> NormalHints<'a> {
pub fn new(xconn: &'a XConnection) -> Self {
NormalHints { size_hints: xconn.alloc_size_hints() }
}
pub fn has_flag(&self, flag: c_long) -> bool {
has_flag(self.size_hints.flags, flag)
}
fn getter(&self, flag: c_long, field1: &c_int, field2: &c_int) -> Option<(u32, u32)> {
if self.has_flag(flag) {
Some((*field1 as _, *field2 as _))
} else {
None
}
}
pub fn get_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PSize, &self.size_hints.width, &self.size_hints.height)
}
// WARNING: This hint is obsolete
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
if let Some((width, height)) = size {
self.size_hints.flags |= ffi::PSize;
self.size_hints.width = width as c_int;
self.size_hints.height = height as c_int;
} else {
self.size_hints.flags &= !ffi::PSize;
}
}
pub fn get_max_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height)
}
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
if let Some((max_width, max_height)) = max_size {
self.size_hints.flags |= ffi::PMaxSize;
self.size_hints.max_width = max_width as c_int;
self.size_hints.max_height = max_height as c_int;
} else {
self.size_hints.flags &= !ffi::PMaxSize;
}
}
pub fn get_min_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height)
}
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
if let Some((min_width, min_height)) = min_size {
self.size_hints.flags |= ffi::PMinSize;
self.size_hints.min_width = min_width as c_int;
self.size_hints.min_height = min_height as c_int;
} else {
self.size_hints.flags &= !ffi::PMinSize;
}
}
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc)
}
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
if let Some((width_inc, height_inc)) = resize_increments {
self.size_hints.flags |= ffi::PResizeInc;
self.size_hints.width_inc = width_inc as c_int;
self.size_hints.height_inc = height_inc as c_int;
} else {
self.size_hints.flags &= !ffi::PResizeInc;
}
}
pub fn get_base_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height)
}
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
if let Some((base_width, base_height)) = base_size {
self.size_hints.flags |= ffi::PBaseSize;
self.size_hints.base_width = base_width as c_int;
self.size_hints.base_height = base_height as c_int;
} else {
self.size_hints.flags &= !ffi::PBaseSize;
}
}
}
impl XConnection {
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
@ -90,4 +183,29 @@ impl XConnection {
}
Flusher::new(self)
}
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> {
let size_hints = self.alloc_size_hints();
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
unsafe {
(self.xlib.XGetWMNormalHints)(
self.display,
window,
size_hints.ptr,
&mut supplied_by_user,
);
}
self.check_errors().map(|_| NormalHints { size_hints })
}
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher {
unsafe {
(self.xlib.XSetWMNormalHints)(
self.display,
window,
normal_hints.size_hints.ptr,
);
}
Flusher::new(self)
}
}

View file

@ -27,10 +27,16 @@ pub use self::wm::*;
use std::mem;
use std::ptr;
use std::ops::BitAnd;
use std::os::raw::*;
use super::{ffi, XConnection, XError};
pub fn reinterpret<'a, A, B>(a: &'a A) -> &'a B {
let b_ptr = a as *const _ as *const B;
unsafe { &*b_ptr }
}
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
let wrapped = Some(value);
if *field != wrapped {
@ -41,6 +47,13 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
}
}
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
Copy + PartialEq + BitAnd<T, Output = T>
{
bitset & flag == flag
}
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
pub struct Flusher<'a> {
xconn: &'a XConnection,

View file

@ -1,21 +1,48 @@
use std::{env, slice};
use std::str::FromStr;
use validate_hidpi_factor;
use super::*;
use super::ffi::{
RROutput,
XRRCrtcInfo,
XRRMonitorInfo,
XRRScreenResources,
};
pub fn calc_dpi_factor(
(width_px, height_px): (u32, u32),
(width_mm, height_mm): (u64, u64),
) -> f64 {
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
let dpi_override = env::var("WINIT_HIDPI_FACTOR")
.ok()
.and_then(|var| f64::from_str(&var).ok());
if let Some(dpi_override) = dpi_override {
if !validate_hidpi_factor(dpi_override) {
panic!(
"`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
dpi_override,
);
}
return dpi_override;
}
// See http://xpra.org/trac/ticket/728 for more information.
if width_mm == 0 || width_mm == 0 {
return 1.0;
}
let ppmm = (
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
// Quantize 1/12 step size
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
assert!(validate_hidpi_factor(dpi_factor));
dpi_factor
}
pub enum MonitorRepr {
Monitor(*mut XRRMonitorInfo),
Crtc(*mut XRRCrtcInfo),
Monitor(*mut ffi::XRRMonitorInfo),
Crtc(*mut ffi::XRRCrtcInfo),
}
impl MonitorRepr {
pub unsafe fn get_output(&self) -> RROutput {
pub unsafe fn get_output(&self) -> ffi::RROutput {
match *self {
// Same member names, but different locations within the struct...
MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
@ -38,47 +65,20 @@ impl MonitorRepr {
}
}
impl From<*mut XRRMonitorInfo> for MonitorRepr {
fn from(monitor: *mut XRRMonitorInfo) -> Self {
impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
MonitorRepr::Monitor(monitor)
}
}
impl From<*mut XRRCrtcInfo> for MonitorRepr {
fn from(crtc: *mut XRRCrtcInfo) -> Self {
impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
MonitorRepr::Crtc(crtc)
}
}
pub fn calc_dpi_factor(
(width_px, height_px): (u32, u32),
(width_mm, height_mm): (u64, u64),
) -> f64 {
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
if let Ok(dpi_factor_str) = env::var("WINIT_HIDPI_FACTOR") {
if let Ok(dpi_factor) = f64::from_str(&dpi_factor_str) {
if dpi_factor <= 0. {
panic!("Expected `WINIT_HIDPI_FACTOR` to be bigger than 0, got '{}'", dpi_factor);
}
return dpi_factor;
}
}
// See http://xpra.org/trac/ticket/728 for more information
if width_mm == 0 || width_mm == 0 {
return 1.0;
}
let ppmm = (
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
// Quantize 1/12 step size
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
}
impl XConnection {
pub unsafe fn get_output_info(&self, resources: *mut XRRScreenResources, repr: &MonitorRepr) -> (String, f32) {
pub unsafe fn get_output_info(&self, resources: *mut ffi::XRRScreenResources, repr: &MonitorRepr) -> (String, f64) {
let output_info = (self.xrandr.XRRGetOutputInfo)(
self.display,
resources,
@ -92,7 +92,7 @@ impl XConnection {
let hidpi_factor = calc_dpi_factor(
repr.get_dimensions(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
) as f32;
);
(self.xrandr.XRRFreeOutputInfo)(output_info);
(name, hidpi_factor)
}

View file

@ -7,15 +7,14 @@ use std::sync::Arc;
use libc;
use parking_lot::Mutex;
use {CursorState, Icon, MouseCursor, WindowAttributes};
use {CursorState, Icon, LogicalPosition, LogicalSize, MouseCursor, WindowAttributes};
use CreationError::{self, OsError};
use platform::MonitorId as PlatformMonitorId;
use platform::PlatformSpecificWindowBuilderAttributes;
use platform::x11::MonitorId as X11MonitorId;
use platform::x11::monitor::get_monitor_for_window;
use window::MonitorId as RootMonitorId;
use super::{ffi, util, XConnection, XError, WindowId, EventsLoop};
use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventsLoop};
unsafe extern "C" fn visibility_predicate(
_display: *mut ffi::Display,
@ -29,7 +28,8 @@ unsafe extern "C" fn visibility_predicate(
#[derive(Debug, Default)]
pub struct SharedState {
pub multitouch: bool,
// Window creation assumes a DPI factor of 1.0, so we use this flag to handle that special case.
pub is_new_window: bool,
pub cursor_pos: Option<(f64, f64)>,
pub size: Option<(u32, u32)>,
pub position: Option<(i32, i32)>,
@ -37,9 +37,19 @@ pub struct SharedState {
pub inner_position_rel_parent: Option<(i32, i32)>,
pub last_monitor: Option<X11MonitorId>,
pub dpi_adjusted: Option<(f64, f64)>,
// Used to restore position after exiting fullscreen.
pub restore_position: Option<(i32, i32)>,
pub frame_extents: Option<util::FrameExtentsHeuristic>,
pub min_dimensions: Option<(u32, u32)>,
pub max_dimensions: Option<(u32, u32)>,
pub min_dimensions: Option<LogicalSize>,
pub max_dimensions: Option<LogicalSize>,
}
impl SharedState {
fn new() -> Mutex<Self> {
let mut shared_state = SharedState::default();
shared_state.is_new_window = true;
Mutex::new(shared_state)
}
}
unsafe impl Send for UnownedWindow {}
@ -52,6 +62,7 @@ pub struct UnownedWindow {
screen_id: i32, // never changes
cursor: Mutex<MouseCursor>,
cursor_state: Mutex<CursorState>,
ime_sender: Mutex<ImeSender>,
pub multitouch: bool, // never changes
pub shared_state: Mutex<SharedState>,
}
@ -65,15 +76,20 @@ impl UnownedWindow {
let xconn = &event_loop.xconn;
let root = event_loop.root;
let max_dimensions: Option<(u32, u32)> = window_attrs.max_dimensions.map(Into::into);
let min_dimensions: Option<(u32, u32)> = window_attrs.min_dimensions.map(Into::into);
let dimensions = {
// x11 only applies constraints when the window is actively resized
// by the user, so we have to manually apply the initial constraints
let mut dimensions = window_attrs.dimensions.unwrap_or((800, 600));
if let Some(max) = window_attrs.max_dimensions {
let mut dimensions = window_attrs.dimensions
.map(Into::into)
.unwrap_or((800, 600));
if let Some(max) = max_dimensions {
dimensions.0 = cmp::min(dimensions.0, max.0);
dimensions.1 = cmp::min(dimensions.1, max.1);
}
if let Some(min) = window_attrs.min_dimensions {
if let Some(min) = min_dimensions {
dimensions.0 = cmp::max(dimensions.0, min.0);
dimensions.1 = cmp::max(dimensions.1, min.1);
}
@ -145,8 +161,9 @@ impl UnownedWindow {
screen_id,
cursor: Default::default(),
cursor_state: Default::default(),
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
multitouch: window_attrs.multitouch,
shared_state: Default::default(),
shared_state: SharedState::new(),
};
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
@ -218,46 +235,24 @@ impl UnownedWindow {
// set size hints
{
(*window.shared_state.lock()).min_dimensions = window_attrs.min_dimensions;
(*window.shared_state.lock()).max_dimensions = window_attrs.max_dimensions;
let mut min_dimensions = window_attrs.min_dimensions;
let mut max_dimensions = window_attrs.max_dimensions;
if !window_attrs.resizable && !util::wm_name_is_one_of(&["Xfwm4"]) {
max_dimensions = Some(dimensions);
min_dimensions = Some(dimensions);
max_dimensions = Some(dimensions.into());
min_dimensions = Some(dimensions.into());
let mut shared_state_lock = window.shared_state.lock();
shared_state_lock.min_dimensions = window_attrs.min_dimensions;
shared_state_lock.max_dimensions = window_attrs.max_dimensions;
}
let mut size_hints = xconn.alloc_size_hints();
(*size_hints).flags = ffi::PSize;
(*size_hints).width = dimensions.0 as c_int;
(*size_hints).height = dimensions.1 as c_int;
if let Some((min_width, min_height)) = min_dimensions {
(*size_hints).flags |= ffi::PMinSize;
(*size_hints).min_width = min_width as c_int;
(*size_hints).min_height = min_height as c_int;
}
if let Some((max_width, max_height)) = max_dimensions {
(*size_hints).flags |= ffi::PMaxSize;
(*size_hints).max_width = max_width as c_int;
(*size_hints).max_height = max_height as c_int;
}
if let Some((width_inc, height_inc)) = pl_attribs.resize_increments {
(*size_hints).flags |= ffi::PResizeInc;
(*size_hints).width_inc = width_inc as c_int;
(*size_hints).height_inc = height_inc as c_int;
}
if let Some((base_width, base_height)) = pl_attribs.base_size {
(*size_hints).flags |= ffi::PBaseSize;
(*size_hints).base_width = base_width as c_int;
(*size_hints).base_height = base_height as c_int;
}
unsafe {
(xconn.xlib.XSetWMNormalHints)(
xconn.display,
window.xwindow,
size_hints.ptr,
);
}//.queue();
let mut normal_hints = util::NormalHints::new(xconn);
normal_hints.set_size(Some(dimensions));
normal_hints.set_min_size(min_dimensions.map(Into::into));
normal_hints.set_max_size(max_dimensions.map(Into::into));
normal_hints.set_resize_increments(pl_attribs.resize_increments);
normal_hints.set_base_size(pl_attribs.base_size);
xconn.set_normal_hints(window.xwindow, normal_hints).queue();
}
// Set window icons
@ -291,7 +286,7 @@ impl UnownedWindow {
&mut supported_ptr,
);
if supported_ptr == ffi::False {
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
return Err(OsError(format!("`XkbSetDetectableAutoRepeat` failed")));
}
}
@ -315,6 +310,15 @@ impl UnownedWindow {
};
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue();
{
let result = event_loop.ime
.borrow_mut()
.create_context(window.xwindow);
if let Err(err) = result {
return Err(OsError(format!("Failed to create input context: {:?}", err)));
}
}
// These properties must be set after mapping
if window_attrs.maximized {
window.set_maximized_inner(window_attrs.maximized).queue();
@ -355,6 +359,16 @@ impl UnownedWindow {
))
}
fn logicalize_coords(&self, (x, y): (i32, i32)) -> LogicalPosition {
let dpi = self.get_hidpi_factor();
LogicalPosition::from_physical((x, y), dpi)
}
fn logicalize_size(&self, (width, height): (u32, u32)) -> LogicalSize {
let dpi = self.get_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") };
@ -400,6 +414,7 @@ impl UnownedWindow {
)
}
#[inline]
pub fn set_urgent(&self, is_urgent: bool) {
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
if is_urgent {
@ -439,17 +454,24 @@ impl UnownedWindow {
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorId>) -> util::Flusher {
match monitor {
None => {
self.set_fullscreen_hint(false)
let flusher = self.set_fullscreen_hint(false);
if let Some(position) = self.shared_state.lock().restore_position.take() {
self.set_position_inner(position.0, position.1).queue();
}
flusher
},
Some(RootMonitorId { inner: PlatformMonitorId::X(monitor) }) => {
let screenpos = monitor.get_position();
self.set_position(screenpos.0 as i32, screenpos.1 as i32);
let window_position = self.get_position_physical();
self.shared_state.lock().restore_position = window_position;
let monitor_origin: (i32, i32) = monitor.get_position().into();
self.set_position_inner(monitor_origin.0, monitor_origin.1).queue();
self.set_fullscreen_hint(true)
}
_ => unreachable!(),
}
}
#[inline]
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
self.set_fullscreen_inner(monitor)
.flush()
@ -459,15 +481,26 @@ impl UnownedWindow {
fn get_rect(&self) -> Option<util::Rect> {
// TODO: This might round-trip more times than needed.
if let (Some(position), Some(size)) = (self.get_position(), self.get_outer_size()) {
if let (Some(position), Some(size)) = (self.get_position_physical(), self.get_outer_size_physical()) {
Some(util::Rect::new(position, size))
} else {
None
}
}
#[inline]
pub fn get_current_monitor(&self) -> X11MonitorId {
get_monitor_for_window(&self.xconn, self.get_rect()).to_owned()
let monitor = self.shared_state
.lock()
.last_monitor
.as_ref()
.cloned();
monitor
.unwrap_or_else(|| {
let monitor = self.xconn.get_monitor_for_window(self.get_rect()).to_owned();
self.shared_state.lock().last_monitor = Some(monitor.clone());
monitor
})
}
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
@ -476,6 +509,7 @@ impl UnownedWindow {
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0))
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
self.set_maximized_inner(maximized)
.flush()
@ -503,6 +537,7 @@ impl UnownedWindow {
}
}
#[inline]
pub fn set_title(&self, title: &str) {
self.set_title_inner(title)
.flush()
@ -526,6 +561,7 @@ impl UnownedWindow {
)
}
#[inline]
pub fn set_decorations(&self, decorations: bool) {
self.set_decorations_inner(decorations)
.flush()
@ -538,6 +574,7 @@ impl UnownedWindow {
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
}
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
self.set_always_on_top_inner(always_on_top)
.flush()
@ -568,6 +605,7 @@ impl UnownedWindow {
)
}
#[inline]
pub fn set_window_icon(&self, icon: Option<Icon>) {
match icon {
Some(icon) => self.set_icon_inner(icon),
@ -575,6 +613,7 @@ impl UnownedWindow {
}.flush().expect("Failed to set icons");
}
#[inline]
pub fn show(&self) {
unsafe {
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
@ -583,6 +622,7 @@ impl UnownedWindow {
}
}
#[inline]
pub fn hide(&self) {
unsafe {
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
@ -596,31 +636,46 @@ impl UnownedWindow {
(*self.shared_state.lock()).frame_extents = Some(extents);
}
pub fn invalidate_cached_frame_extents(&self) {
pub(crate) fn invalidate_cached_frame_extents(&self) {
(*self.shared_state.lock()).frame_extents.take();
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> {
let extents = (*self.shared_state.lock()).frame_extents.clone();
if let Some(extents) = extents {
self.get_inner_position().map(|(x, y)|
extents.inner_pos_to_outer(x, y)
)
self.get_inner_position_physical()
.map(|(x, y)| extents.inner_pos_to_outer(x, y))
} else {
self.update_cached_frame_extents();
self.get_position_physical()
}
}
#[inline]
pub fn get_position(&self) -> Option<LogicalPosition> {
let extents = (*self.shared_state.lock()).frame_extents.clone();
if let Some(extents) = extents {
self.get_inner_position()
.map(|logical| extents.inner_pos_to_outer_logical(logical, self.get_hidpi_factor()))
} else {
self.update_cached_frame_extents();
self.get_position()
}
}
#[inline]
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
self.xconn.translate_coords(self.xwindow, self.root )
pub(crate) fn get_inner_position_physical(&self) -> Option<(i32, i32)> {
self.xconn.translate_coords(self.xwindow, self.root)
.ok()
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
}
pub fn set_position(&self, mut x: i32, mut y: i32) {
#[inline]
pub fn get_inner_position(&self) -> Option<LogicalPosition> {
self.get_inner_position_physical()
.map(|coords| self.logicalize_coords(coords))
}
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher {
// There are a few WMs that set client area position rather than window position, so
// we'll translate for consistency.
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
@ -630,7 +685,7 @@ impl UnownedWindow {
y += extents.frame_extents.top as i32;
} else {
self.update_cached_frame_extents();
self.set_position(x, y)
return self.set_position_inner(x, y);
}
}
unsafe {
@ -640,32 +695,58 @@ impl UnownedWindow {
x as c_int,
y as c_int,
);
self.xconn.flush_requests()
}.expect("Failed to call XMoveWindow");
}
util::Flusher::new(&self.xconn)
}
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
self.set_position_inner(x, y)
.flush()
.expect("Failed to call `XMoveWindow`");
}
#[inline]
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
pub fn set_position(&self, logical_position: LogicalPosition) {
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
self.set_position_physical(x, y);
}
pub(crate) fn get_inner_size_physical(&self) -> Option<(u32, u32)> {
self.xconn.get_geometry(self.xwindow)
.ok()
.map(|geo| (geo.width, geo.height))
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
let extents = (*self.shared_state.lock()).frame_extents.clone();
pub fn get_inner_size(&self) -> Option<LogicalSize> {
self.get_inner_size_physical()
.map(|size| self.logicalize_size(size))
}
pub(crate) fn get_outer_size_physical(&self) -> Option<(u32, u32)> {
let extents = self.shared_state.lock().frame_extents.clone();
if let Some(extents) = extents {
self.get_inner_size().map(|(w, h)|
extents.inner_size_to_outer(w, h)
)
self.get_inner_size_physical()
.map(|(w, h)| extents.inner_size_to_outer(w, h))
} else {
self.update_cached_frame_extents();
self.get_outer_size_physical()
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<LogicalSize> {
let extents = self.shared_state.lock().frame_extents.clone();
if let Some(extents) = extents {
self.get_inner_size()
.map(|logical| extents.inner_size_to_outer_logical(logical, self.get_hidpi_factor()))
} else {
self.update_cached_frame_extents();
self.get_outer_size()
}
}
#[inline]
pub fn set_inner_size(&self, width: u32, height: u32) {
pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
@ -674,62 +755,86 @@ impl UnownedWindow {
height as c_uint,
);
self.xconn.flush_requests()
}.expect("Failed to call XResizeWindow");
}.expect("Failed to call `XResizeWindow`");
}
unsafe fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
where F: FnOnce(*mut ffi::XSizeHints) -> ()
#[inline]
pub fn set_inner_size(&self, logical_size: LogicalSize) {
let dpi_factor = self.get_hidpi_factor();
let (width, height) = logical_size.to_physical(dpi_factor).into();
self.set_inner_size_physical(width, height);
}
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
where F: FnOnce(&mut util::NormalHints) -> ()
{
let size_hints = self.xconn.alloc_size_hints();
let mut flags: c_long = mem::uninitialized();
(self.xconn.xlib.XGetWMNormalHints)(
self.xconn.display,
self.xwindow,
size_hints.ptr,
&mut flags,
);
self.xconn.check_errors()?;
callback(size_hints.ptr);
(self.xconn.xlib.XSetWMNormalHints)(
self.xconn.display,
self.xwindow,
size_hints.ptr,
);
self.xconn.flush_requests()?;
Ok(())
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
callback(&mut normal_hints);
self.xconn.set_normal_hints(self.xwindow, normal_hints).flush()
}
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
(*self.shared_state.lock()).min_dimensions = dimensions;
unsafe {
self.update_normal_hints(|size_hints| {
if let Some((width, height)) = dimensions {
(*size_hints).flags |= ffi::PMinSize;
(*size_hints).min_width = width as c_int;
(*size_hints).min_height = height as c_int;
} else {
(*size_hints).flags &= !ffi::PMinSize;
}
})
}.expect("Failed to call XSetWMNormalHints");
pub(crate) fn set_min_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions))
.expect("Failed to call `XSetWMNormalHints`");
}
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
(*self.shared_state.lock()).max_dimensions = dimensions;
#[inline]
pub fn set_min_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().min_dimensions = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
});
self.set_min_dimensions_physical(physical_dimensions);
}
pub(crate) fn set_max_dimensions_physical(&self, dimensions: Option<(u32, u32)>) {
self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions))
.expect("Failed to call `XSetWMNormalHints`");
}
#[inline]
pub fn set_max_dimensions(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().max_dimensions = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
logical_dimensions.to_physical(self.get_hidpi_factor()).into()
});
self.set_max_dimensions_physical(physical_dimensions);
}
pub(crate) fn adjust_for_dpi(
&self,
old_dpi_factor: f64,
new_dpi_factor: f64,
width: f64,
height: f64,
) -> (f64, f64, util::Flusher) {
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;
let new_height = height as f64 * scale_factor;
(new_width.round() as u32, new_height.round() as u32)
};
let max_size = normal_hints.get_max_size().map(&dpi_adjuster);
let min_size = normal_hints.get_min_size().map(&dpi_adjuster);
let resize_increments = normal_hints.get_resize_increments().map(&dpi_adjuster);
let base_size = normal_hints.get_base_size().map(&dpi_adjuster);
normal_hints.set_max_size(max_size);
normal_hints.set_min_size(min_size);
normal_hints.set_resize_increments(resize_increments);
normal_hints.set_base_size(base_size);
}).expect("Failed to update normal hints");
unsafe {
self.update_normal_hints(|size_hints| {
if let Some((width, height)) = dimensions {
(*size_hints).flags |= ffi::PMaxSize;
(*size_hints).max_width = width as c_int;
(*size_hints).max_height = height as c_int;
} else {
(*size_hints).flags &= !ffi::PMaxSize;
}
})
}.expect("Failed to call XSetWMNormalHints");
(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))
}
pub fn set_resizable(&self, resizable: bool) {
@ -739,24 +844,26 @@ impl UnownedWindow {
// the lesser of two evils and do nothing.
return;
}
if resizable {
let min_dimensions = (*self.shared_state.lock()).min_dimensions;
let max_dimensions = (*self.shared_state.lock()).max_dimensions;
self.set_min_dimensions(min_dimensions);
self.set_max_dimensions(max_dimensions);
let (logical_min, logical_max) = if resizable {
let shared_state_lock = self.shared_state.lock();
(shared_state_lock.min_dimensions, shared_state_lock.max_dimensions)
} else {
unsafe {
self.update_normal_hints(|size_hints| {
(*size_hints).flags |= ffi::PMinSize | ffi::PMaxSize;
if let Some((width, height)) = self.get_inner_size() {
(*size_hints).min_width = width as c_int;
(*size_hints).min_height = height as c_int;
(*size_hints).max_width = width as c_int;
(*size_hints).max_height = height as c_int;
}
})
}.expect("Failed to call XSetWMNormalHints");
}
let window_size = self.get_inner_size();
(window_size.clone(), window_size)
};
let dpi_factor = self.get_hidpi_factor();
let min_dimensions = logical_min
.map(|logical_size| logical_size.to_physical(dpi_factor))
.map(Into::into);
let max_dimensions = logical_max
.map(|logical_size| logical_size.to_physical(dpi_factor))
.map(Into::into);
self.update_normal_hints(|normal_hints| {
normal_hints.set_min_size(min_dimensions);
normal_hints.set_max_size(max_dimensions);
}).expect("Failed to call `XSetWMNormalHints`");
}
#[inline]
@ -774,21 +881,12 @@ impl UnownedWindow {
Arc::clone(&self.xconn)
}
#[inline]
pub fn platform_display(&self) -> *mut libc::c_void {
self.xconn.display as _
}
#[inline]
pub fn get_xlib_window(&self) -> c_ulong {
self.xwindow
}
#[inline]
pub fn platform_window(&self) -> *mut libc::c_void {
self.xwindow as _
}
pub fn get_xcb_connection(&self) -> *mut c_void {
unsafe {
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
@ -886,6 +984,7 @@ impl UnownedWindow {
}
}
#[inline]
pub fn set_cursor(&self, cursor: MouseCursor) {
*self.cursor.lock() = cursor;
if *self.cursor_state.lock() != CursorState::Hide {
@ -931,6 +1030,7 @@ impl UnownedWindow {
Some(cursor)
}
#[inline]
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::*;
@ -994,11 +1094,12 @@ impl UnownedWindow {
}
}
pub fn hidpi_factor(&self) -> f32 {
#[inline]
pub fn get_hidpi_factor(&self) -> f64 {
self.get_current_monitor().hidpi_factor
}
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
pub(crate) fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ()> {
unsafe {
(self.xconn.xlib.XWarpPointer)(
self.xconn.display,
@ -1015,6 +1116,24 @@ impl UnownedWindow {
}
}
#[inline]
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ()> {
let (x, y) = logical_position.to_physical(self.get_hidpi_factor()).into();
self.set_cursor_position_physical(x, y)
}
pub(crate) fn set_ime_spot_physical(&self, x: i32, y: i32) {
let _ = self.ime_sender
.lock()
.send((self.xwindow, x as i16, y as i16));
}
#[inline]
pub fn set_ime_spot(&self, logical_spot: LogicalPosition) {
let (x, y) = logical_spot.to_physical(self.get_hidpi_factor()).into();
self.set_ime_spot_physical(x, y);
}
#[inline]
pub fn id(&self) -> WindowId { WindowId(self.xwindow) }
}