winit-sonoma-fix/src/platform/linux/wayland/window.rs

284 lines
8.5 KiB
Rust
Raw Normal View History

2016-10-08 23:51:29 +11:00
use std::sync::{Arc, Mutex};
use std::sync::atomic::{Ordering, AtomicBool};
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
use std::cmp;
use wayland_client::{EventQueueHandle, Proxy};
use wayland_client::protocol::{wl_display,wl_surface};
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
use {CreationError, MouseCursor, CursorState, WindowAttributes};
2016-10-08 23:51:29 +11:00
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
use platform::wayland::MonitorId as WaylandMonitorId;
use platform::wayland::context::get_available_monitors;
2017-03-05 00:04:01 +11:00
use super::{WaylandContext, EventsLoop};
2016-10-08 23:51:29 +11:00
use super::wayland_window;
use super::wayland_window::DecoratedSurface;
pub struct Window {
2017-03-11 09:56:31 +11:00
// the global wayland context
2016-10-08 23:51:29 +11:00
ctxt: Arc<WaylandContext>,
2017-03-11 09:56:31 +11:00
// signal to advertize the EventsLoop when we are destroyed
cleanup_signal: Arc<AtomicBool>,
2017-03-11 09:56:31 +11:00
// our wayland surface
2016-10-08 23:51:29 +11:00
surface: Arc<wl_surface::WlSurface>,
2017-03-11 09:56:31 +11:00
// our current inner dimensions
2016-10-08 23:51:29 +11:00
size: Mutex<(u32, u32)>,
2017-03-11 09:56:31 +11:00
// the id of our DecoratedHandler in the EventQueue
2016-10-08 23:51:29 +11:00
decorated_id: usize
}
2017-03-05 00:04:01 +11:00
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
2017-03-05 00:04:01 +11:00
#[inline]
pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
WindowId(s.ptr() as usize)
}
impl Window {
pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result<Window, CreationError>
{
let ctxt = evlp.context().clone();
2016-10-08 23:51:29 +11:00
let (width, height) = attributes.dimensions.unwrap_or((800,600));
let (surface, decorated, xdg) = ctxt.create_window::<DecoratedHandler>(width, height, attributes.decorations);
2016-10-08 23:51:29 +11:00
// init DecoratedSurface
let cleanup_signal = evlp.get_window_init();
let mut fullscreen_monitor = None;
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen {
ctxt.with_output(monitor_id.clone(), |output| {
fullscreen_monitor = output.clone();
});
}
2017-03-05 00:04:01 +11:00
let decorated_id = {
let mut evq_guard = ctxt.evq.lock().unwrap();
// store the DecoratedSurface handler
2017-03-05 00:04:01 +11:00
let decorated_id = evq_guard.add_handler_with_init(decorated);
{
let mut state = evq_guard.state();
// initialize the DecoratedHandler
2017-03-05 00:04:01 +11:00
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
*(decorated.handler()) = Some(DecoratedHandler::new(!xdg));
2017-03-05 00:04:01 +11:00
2017-03-11 09:56:31 +11:00
// set fullscreen if necessary
if let Some(output) = fullscreen_monitor {
decorated.set_fullscreen(Some(&output));
2017-03-05 00:04:01 +11:00
}
// Finally, set the decorations size
decorated.resize(width as i32, height as i32);
2016-10-08 23:51:29 +11:00
}
evq_guard.sync_roundtrip().unwrap();
2017-03-05 00:04:01 +11:00
decorated_id
};
// send our configuration to the compositor
// if we're in xdg mode, no buffer is attached yet, so this
// is fine (and more or less required actually)
surface.commit();
2017-03-05 00:04:01 +11:00
let me = Window {
2016-10-08 23:51:29 +11:00
ctxt: ctxt,
cleanup_signal: cleanup_signal,
2016-10-08 23:51:29 +11:00
surface: surface,
size: Mutex::new((width, height)),
decorated_id: decorated_id
};
2017-03-11 09:56:31 +11:00
// register ourselves to the EventsLoop
evlp.register_window(me.decorated_id, me.surface.clone());
2016-10-08 23:51:29 +11:00
2017-03-05 00:04:01 +11:00
Ok(me)
}
2016-10-08 23:51:29 +11:00
2017-03-05 00:04:01 +11:00
#[inline]
pub fn id(&self) -> WindowId {
make_wid(&self.surface)
}
pub fn set_title(&self, title: &str) {
let mut guard = self.ctxt.evq.lock().unwrap();
2016-10-10 01:19:06 +11:00
let mut state = guard.state();
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
2016-10-10 01:19:06 +11:00
decorated.set_title(title.into())
}
#[inline]
pub fn show(&self) {
// TODO
}
#[inline]
pub fn hide(&self) {
// TODO
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
// Not possible with wayland
None
}
#[inline]
pub fn set_position(&self, _x: i32, _y: i32) {
// Not possible with wayland
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
2016-10-10 01:19:06 +11:00
Some(self.size.lock().unwrap().clone())
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
2016-10-10 01:19:06 +11:00
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))
}
#[inline]
2016-10-10 01:19:06 +11:00
// 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) {
let mut guard = self.ctxt.evq.lock().unwrap();
2016-10-10 01:19:06 +11:00
let mut state = guard.state();
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.resize(x as i32, y as i32);
*(self.size.lock().unwrap()) = (x, y);
}
#[inline]
2015-12-13 23:13:20 +11:00
pub fn set_cursor(&self, _cursor: MouseCursor) {
// TODO
}
#[inline]
2015-12-23 00:35:55 +11:00
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
use CursorState::{Grab, Normal, Hide};
// TODO : not yet possible on wayland to grab cursor
match state {
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
Normal => Ok(())
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
// TODO
1.0
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
// TODO: not yet possible on wayland
2015-12-23 00:35:55 +11:00
Err(())
}
pub fn get_display(&self) -> &wl_display::WlDisplay {
2016-10-08 23:51:29 +11:00
&self.ctxt.display
}
pub fn get_surface(&self) -> &wl_surface::WlSurface {
2016-10-08 23:51:29 +11:00
&self.surface
}
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 18:33:46 +10:00
pub fn get_current_monitor(&self) -> WaylandMonitorId {
let monitors = get_available_monitors(&self.ctxt);
let default = monitors[0].clone();
let (wx,wy) = match self.get_position() {
Some(val) => (cmp::max(0,val.0) as u32, cmp::max(0,val.1) as u32),
None=> return default,
};
let (ww,wh) = match self.get_outer_size() {
Some(val) => val,
None=> return default,
};
// Opposite corner coordinates
let (wxo, wyo) = (wx+ww-1, wy+wh-1);
// Find the monitor with the biggest overlap with the window
let mut overlap = 0;
let mut find = default;
for monitor in monitors {
let (mx, my) = monitor.get_position();
let (mw, mh) = monitor.get_dimensions();
let (mxo, myo) = (mx+mw-1, my+mh-1);
let (ox, oy) = (cmp::max(wx, mx), cmp::max(wy, my));
let (oxo, oyo) = (cmp::min(wxo, mxo), cmp::min(wyo, myo));
let osize = if ox <= oxo || oy <= oyo { 0 } else { (oxo-ox)*(oyo-oy) };
if osize > overlap {
overlap = osize;
find = monitor;
}
}
find
}
pub fn is_ready(&self) -> bool {
let mut guard = self.ctxt.evq.lock().unwrap();
let mut state = guard.state();
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
decorated.handler().as_ref().unwrap().configured
}
}
impl Drop for Window {
fn drop(&mut self) {
2016-10-08 23:51:29 +11:00
self.surface.destroy();
self.cleanup_signal.store(true, Ordering::Relaxed);
2016-10-08 23:51:29 +11:00
}
}
2017-03-05 00:04:01 +11:00
pub struct DecoratedHandler {
newsize: Option<(u32, u32)>,
refresh: bool,
closed: bool,
configured: bool
2016-10-08 23:51:29 +11:00
}
impl DecoratedHandler {
fn new(configured: bool) -> DecoratedHandler {
DecoratedHandler {
newsize: None,
refresh: false,
closed: false,
configured: configured
}
}
2017-03-05 00:04:01 +11:00
pub fn take_newsize(&mut self) -> Option<(u32, u32)> {
2016-10-08 23:51:29 +11:00
self.newsize.take()
}
pub fn take_refresh(&mut self) -> bool {
let refresh = self.refresh;
self.refresh = false;
refresh
}
pub fn is_closed(&self) -> bool { self.closed }
2016-10-08 23:51:29 +11:00
}
impl wayland_window::Handler for DecoratedHandler {
fn configure(&mut self,
_: &mut EventQueueHandle,
_cfg: wayland_window::Configure,
newsize: Option<(i32, i32)>)
2016-10-08 23:51:29 +11:00
{
self.newsize = newsize.map(|(w, h)| (w as u32, h as u32));
self.refresh = true;
self.configured = true;
2016-10-08 23:51:29 +11:00
}
fn close(&mut self, _: &mut EventQueueHandle) {
self.closed = true;
}
}