Merge branch 'master' into web

This commit is contained in:
Ryan G 2019-10-12 23:51:34 -04:00 committed by GitHub
commit f62bb33317
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 449 additions and 102 deletions

View file

@ -1,6 +1,7 @@
# Unreleased
- Add web support via the 'stdweb' or 'web-sys' features
- On Windows, implemented function to get HINSTANCE
- On macOS, implement `run_return`.
- On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`.
- On X11, performance is improved when rapidly calling `Window::set_cursor_icon`.
@ -21,7 +22,17 @@
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
- On X11, prevent stealing input focus when creating a new window.
Only steal input focus when entering fullscreen mode.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
- Removed `derivative` crate dependency.
- On Wayland, add support for set_cursor_icon.
- Use `impl Iterator<Item = MonitorHandle>` instead of `AvailableMonitorsIter` consistently.
- On macOS, fix fullscreen state being updated after entering fullscreen instead of before,
resulting in `Window::fullscreen` returning the old state in `Resized` events instead of
reflecting the new fullscreen state
- On X11, fix use-after-free during window creation
- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen.
- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor.
# 0.20.0 Alpha 3 (2019-08-14)

View file

@ -24,8 +24,7 @@ lazy_static = "1"
libc = "0.2"
log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] }
derivative = "1.0.2"
raw-window-handle = "0.2"
raw-window-handle = "0.3"
[dev-dependencies]
image = "0.21"

View file

@ -4,6 +4,6 @@ fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
dbg!(window.available_monitors());
dbg!(window.available_monitors().collect::<Vec<_>>());
dbg!(window.primary_monitor());
}

View file

@ -13,9 +13,7 @@ use instant::Instant;
use std::ops::Deref;
use std::{error, fmt};
use crate::event::Event;
use crate::monitor::{AvailableMonitorsIter, MonitorHandle};
use crate::platform_impl;
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
/// Provides a way to retrieve events from the system and from the windows that were registered to
/// the events loop.
@ -150,10 +148,10 @@ impl<T> EventLoop<T> {
/// Returns the list of all the monitors available on the system.
#[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
let data = self.event_loop.available_monitors();
AvailableMonitorsIter {
data: data.into_iter(),
}
self.event_loop
.available_monitors()
.into_iter()
.map(|inner| MonitorHandle { inner })
}
/// Returns the primary monitor of the system.

View file

@ -121,8 +121,6 @@ extern crate log;
#[macro_use]
extern crate serde;
#[macro_use]
extern crate derivative;
#[macro_use]
#[cfg(any(target_os = "ios", target_os = "windows"))]
extern crate bitflags;
#[cfg(any(target_os = "macos", target_os = "ios"))]

View file

@ -1,63 +1,36 @@
//! Types useful for interacting with a user's monitors.
//!
//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_id]
//! type. This is retreived from an [`AvailableMonitorsIter`][monitor_iter], which can be acquired
//! with:
//! type. This is retreived from one of the following methods, which return an iterator of
//! [`MonitorHandle`][monitor_id]:
//! - [`EventLoop::available_monitors`][loop_get]
//! - [`Window::available_monitors`][window_get].
//!
//! [monitor_id]: ./struct.MonitorHandle.html
//! [monitor_iter]: ./struct.AvailableMonitorsIter.html
//! [loop_get]: ../event_loop/struct.EventLoop.html#method.available_monitors
//! [window_get]: ../window/struct.Window.html#method.available_monitors
use std::collections::vec_deque::IntoIter as VecDequeIter;
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
platform_impl,
};
/// An iterator over all available monitors.
///
/// Can be acquired with:
/// - [`EventLoop::available_monitors`][loop_get]
/// - [`Window::available_monitors`][window_get].
///
/// [loop_get]: ../event_loop/struct.EventLoop.html#method.available_monitors
/// [window_get]: ../window/struct.Window.html#method.available_monitors
// Implementation note: we retrieve the list once, then serve each element by one by one.
// This may change in the future.
#[derive(Debug)]
pub struct AvailableMonitorsIter {
pub(crate) data: VecDequeIter<platform_impl::MonitorHandle>,
}
impl Iterator for AvailableMonitorsIter {
type Item = MonitorHandle;
#[inline]
fn next(&mut self) -> Option<MonitorHandle> {
self.data.next().map(|id| MonitorHandle { inner: id })
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.data.size_hint()
}
}
/// Describes a fullscreen video mode of a monitor.
///
/// Can be acquired with:
/// - [`MonitorHandle::video_modes`][monitor_get].
///
/// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes
#[derive(Derivative)]
#[derivative(Clone, Debug = "transparent", PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct VideoMode {
pub(crate) video_mode: platform_impl::VideoMode,
}
impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.video_mode.fmt(f)
}
}
impl PartialOrd for VideoMode {
fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))

View file

@ -34,6 +34,8 @@ impl<T> EventLoopExtWindows for EventLoop<T> {
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExtWindows {
/// Returns the HINSTANCE of the window
fn hinstance(&self) -> *mut libc::c_void;
/// Returns the native handle that is used by this window.
///
/// The pointer will become invalid when the native window was destroyed.
@ -44,6 +46,11 @@ pub trait WindowExtWindows {
}
impl WindowExtWindows for Window {
#[inline]
fn hinstance(&self) -> *mut libc::c_void {
self.window.hinstance() as *mut _
}
#[inline]
fn hwnd(&self) -> *mut libc::c_void {
self.window.hwnd() as *mut _

View file

@ -7,11 +7,19 @@ use std::{
time::Instant,
};
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
};
use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
use smithay_client_toolkit::pointer::{AutoPointer, AutoThemer};
use smithay_client_toolkit::reexports::client::protocol::{
wl_compositor::WlCompositor, wl_shm::WlShm, wl_surface::WlSurface,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::ModifiersState,
@ -21,6 +29,7 @@ use crate::{
sticky_exit_callback, MonitorHandle as PlatformMonitorHandle,
VideoMode as PlatformVideoMode,
},
window::CursorIcon,
};
use super::{window::WindowStore, DeviceId, WindowId};
@ -69,16 +78,159 @@ impl<T> WindowEventsSink<T> {
}
}
pub struct CursorManager {
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
auto_themer: Option<AutoThemer>,
pointers: Vec<AutoPointer>,
locked_pointers: Vec<ZwpLockedPointerV1>,
cursor_visible: bool,
current_cursor: CursorIcon,
}
impl CursorManager {
fn new(constraints: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>) -> CursorManager {
CursorManager {
pointer_constraints_proxy: constraints,
auto_themer: None,
pointers: Vec::new(),
locked_pointers: Vec::new(),
cursor_visible: true,
current_cursor: CursorIcon::default(),
}
}
fn register_pointer(&mut self, pointer: wl_pointer::WlPointer) {
let auto_themer = self
.auto_themer
.as_ref()
.expect("AutoThemer not initialized. Server did not advertise shm or compositor?");
self.pointers.push(auto_themer.theme_pointer(pointer));
}
fn set_auto_themer(&mut self, auto_themer: AutoThemer) {
self.auto_themer = Some(auto_themer);
}
pub fn set_cursor_visible(&mut self, visible: bool) {
if !visible {
for pointer in self.pointers.iter() {
(**pointer).set_cursor(0, None, 0, 0);
}
} else {
self.set_cursor_icon_impl(self.current_cursor);
}
self.cursor_visible = visible;
}
/// A helper function to restore cursor styles on PtrEvent::Enter.
pub fn reload_cursor_style(&mut self) {
if !self.cursor_visible {
self.set_cursor_visible(false);
} else {
self.set_cursor_icon_impl(self.current_cursor);
}
}
pub fn set_cursor_icon(&mut self, cursor: CursorIcon) {
if self.cursor_visible && cursor != self.current_cursor {
self.current_cursor = cursor;
self.set_cursor_icon_impl(cursor);
}
}
fn set_cursor_icon_impl(&mut self, cursor: CursorIcon) {
let cursor = match cursor {
CursorIcon::Alias => "link",
CursorIcon::Arrow => "arrow",
CursorIcon::Cell => "plus",
CursorIcon::Copy => "copy",
CursorIcon::Crosshair => "crosshair",
CursorIcon::Default => "left_ptr",
CursorIcon::Hand => "hand",
CursorIcon::Help => "question_arrow",
CursorIcon::Move => "move",
CursorIcon::Grab => "grab",
CursorIcon::Grabbing => "grabbing",
CursorIcon::Progress => "progress",
CursorIcon::AllScroll => "all-scroll",
CursorIcon::ContextMenu => "context-menu",
CursorIcon::NoDrop => "no-drop",
CursorIcon::NotAllowed => "crossed_circle",
// Resize cursors
CursorIcon::EResize => "right_side",
CursorIcon::NResize => "top_side",
CursorIcon::NeResize => "top_right_corner",
CursorIcon::NwResize => "top_left_corner",
CursorIcon::SResize => "bottom_side",
CursorIcon::SeResize => "bottom_right_corner",
CursorIcon::SwResize => "bottom_left_corner",
CursorIcon::WResize => "left_side",
CursorIcon::EwResize => "h_double_arrow",
CursorIcon::NsResize => "v_double_arrow",
CursorIcon::NwseResize => "bd_double_arrow",
CursorIcon::NeswResize => "fd_double_arrow",
CursorIcon::ColResize => "h_double_arrow",
CursorIcon::RowResize => "v_double_arrow",
CursorIcon::Text => "text",
CursorIcon::VerticalText => "vertical-text",
CursorIcon::Wait => "watch",
CursorIcon::ZoomIn => "zoom-in",
CursorIcon::ZoomOut => "zoom-out",
};
for pointer in self.pointers.iter() {
// Ignore erros, since we don't want to fail hard in case we can't find a proper cursor
// in a given theme.
let _ = pointer.set_cursor(cursor, None);
}
}
pub fn grab_pointer(&mut self, surface: Option<&WlSurface>) {
for locked_pointer in self.locked_pointers.drain(..) {
locked_pointer.destroy();
}
if let Some(surface) = surface {
for pointer in self.pointers.iter() {
let locked_pointer = self
.pointer_constraints_proxy
.try_lock()
.unwrap()
.as_ref()
.and_then(|pointer_constraints| {
super::pointer::implement_locked_pointer(
surface,
&**pointer,
pointer_constraints,
)
.ok()
});
if let Some(locked_pointer) = locked_pointer {
self.locked_pointers.push(locked_pointer);
}
}
}
}
}
pub struct EventLoop<T: 'static> {
// The loop
inner_loop: ::calloop::EventLoop<()>,
// The wayland display
pub display: Arc<Display>,
// the output manager
// The output manager
pub outputs: OutputMgr,
// our sink, shared with some handlers, buffering the events
// Our sink, shared with some handlers, buffering the events
sink: Arc<Mutex<WindowEventsSink<T>>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
// Utility for grabbing the cursor and changing visibility
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
user_sender: ::calloop::channel::Sender<T>,
_kbd_source: ::calloop::Source<
@ -91,17 +243,19 @@ pub struct EventLoop<T: 'static> {
//
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
pub struct EventLoopProxy<T: 'static> {
user_sender: ::calloop::channel::Sender<T>,
user_sender: calloop::channel::Sender<T>,
}
pub struct EventLoopWindowTarget<T> {
// the event queue
// The event queue
pub evq: RefCell<::calloop::Source<EventQueue>>,
// The window store
pub store: Arc<Mutex<WindowStore>>,
// the env
// The cursor manager
pub cursor_manager: Arc<Mutex<CursorManager>>,
// The env
pub env: Environment,
// a cleanup switch to prune dead windows
// A cleanup switch to prune dead windows
pub cleanup_needed: Arc<Mutex<bool>>,
// The wayland display
pub display: Arc<Display>,
@ -146,14 +300,24 @@ impl<T: 'static> EventLoop<T> {
})
.unwrap();
let pointer_constraints_proxy = Arc::new(Mutex::new(None));
let mut seat_manager = SeatManager {
sink: sink.clone(),
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
store: store.clone(),
seats: seats.clone(),
kbd_sender,
cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))),
};
let cursor_manager = seat_manager.cursor_manager.clone();
let cursor_manager_clone = cursor_manager.clone();
let shm_cell = Rc::new(RefCell::new(None));
let compositor_cell = Rc::new(RefCell::new(None));
let env = Environment::from_display_with_cb(
&display,
&mut event_queue,
@ -175,6 +339,42 @@ impl<T: 'static> EventLoop<T> {
.try_borrow_mut()
.unwrap() = Some(relative_pointer_manager_proxy);
}
if interface == "zwp_pointer_constraints_v1" {
let pointer_constraints_proxy = registry
.bind(version, id, move |pointer_constraints| {
pointer_constraints.implement_closure(|_, _| (), ())
})
.unwrap();
*seat_manager.pointer_constraints_proxy.lock().unwrap() =
Some(pointer_constraints_proxy);
}
if interface == "wl_shm" {
let shm: WlShm = registry
.bind(version, id, move |shm| shm.implement_closure(|_, _| (), ()))
.unwrap();
(*shm_cell.borrow_mut()) = Some(shm);
}
if interface == "wl_compositor" {
let compositor: WlCompositor = registry
.bind(version, id, move |compositor| {
compositor.implement_closure(|_, _| (), ())
})
.unwrap();
(*compositor_cell.borrow_mut()) = Some(compositor);
}
if compositor_cell.borrow().is_some() && shm_cell.borrow().is_some() {
let compositor = compositor_cell.borrow_mut().take().unwrap();
let shm = shm_cell.borrow_mut().take().unwrap();
let auto_themer = AutoThemer::init(None, compositor, &shm);
cursor_manager_clone
.lock()
.unwrap()
.set_auto_themer(auto_themer);
}
if interface == "wl_seat" {
seat_manager.add_seat(id, version, registry)
}
@ -207,6 +407,7 @@ impl<T: 'static> EventLoop<T> {
})
.unwrap();
let cursor_manager_clone = cursor_manager.clone();
Ok(EventLoop {
inner_loop,
sink,
@ -221,6 +422,7 @@ impl<T: 'static> EventLoop<T> {
evq: RefCell::new(source),
store,
env,
cursor_manager: cursor_manager_clone,
cleanup_needed: Arc::new(Mutex::new(false)),
seats,
display,
@ -242,7 +444,7 @@ impl<T: 'static> EventLoop<T> {
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
::std::process::exit(0);
std::process::exit(0);
}
pub fn run_return<F>(&mut self, mut callback: F)
@ -497,6 +699,8 @@ struct SeatManager<T: 'static> {
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatManager<T> {
@ -513,6 +717,7 @@ impl<T: 'static> SeatManager<T> {
touch: None,
kbd_sender: self.kbd_sender.clone(),
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
cursor_manager: self.cursor_manager.clone(),
};
let seat = registry
.bind(min(version, 5), id, move |seat| {
@ -544,6 +749,7 @@ struct SeatData<T> {
keyboard: Option<wl_keyboard::WlKeyboard>,
touch: Option<wl_touch::WlTouch>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatData<T> {
@ -558,8 +764,14 @@ impl<T: 'static> SeatData<T> {
self.sink.clone(),
self.store.clone(),
self.modifiers_tracker.clone(),
self.cursor_manager.clone(),
));
self.cursor_manager
.lock()
.unwrap()
.register_pointer(self.pointer.as_ref().unwrap().clone());
self.relative_pointer = self
.relative_pointer_manager_proxy
.try_borrow()

View file

@ -5,7 +5,11 @@ use crate::event::{
WindowEvent,
};
use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId};
use super::{
event_loop::{CursorManager, WindowEventsSink},
window::WindowStore,
DeviceId,
};
use smithay_client_toolkit::reexports::client::protocol::{
wl_pointer::{self, Event as PtrEvent, WlPointer},
@ -17,11 +21,19 @@ use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::Lifetime,
zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
};
use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface;
pub fn implement_pointer<T: 'static>(
seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
cursor_manager: Arc<Mutex<CursorManager>>,
) -> WlPointer {
seat.get_pointer(|pointer| {
let mut mouse_focus = None;
@ -33,6 +45,7 @@ pub fn implement_pointer<T: 'static>(
move |evt, pointer| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
let mut cursor_manager = cursor_manager.lock().unwrap();
match evt {
PtrEvent::Enter {
surface,
@ -62,6 +75,8 @@ pub fn implement_pointer<T: 'static>(
wid,
);
}
cursor_manager.reload_cursor_style();
}
PtrEvent::Leave { surface, .. } => {
mouse_focus = None;
@ -241,3 +256,13 @@ pub fn implement_relative_pointer<T: 'static>(
)
})
}
pub fn implement_locked_pointer(
surface: &WlSurface,
pointer: &WlPointer,
constraints: &ZwpPointerConstraintsV1,
) -> Result<ZwpLockedPointerV1, ()> {
constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent.to_raw(), |c| {
c.implement_closure(|_, _| (), ())
})
}

View file

@ -1,6 +1,7 @@
use raw_window_handle::unix::WaylandHandle;
use std::{
collections::VecDeque,
mem::replace,
sync::{Arc, Mutex, Weak},
};
@ -26,11 +27,12 @@ use smithay_client_toolkit::{
window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow},
};
use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId};
use super::{event_loop::CursorManager, make_wid, EventLoopWindowTarget, MonitorHandle, WindowId};
pub struct Window {
surface: wl_surface::WlSurface,
frame: Arc<Mutex<SWindow<ConceptFrame>>>,
cursor_manager: Arc<Mutex<CursorManager>>,
outputs: OutputMgr, // Access to info for all monitors
size: Arc<Mutex<(u32, u32)>>,
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
@ -52,6 +54,7 @@ impl Window {
let fullscreen = Arc::new(Mutex::new(false));
let window_store = evlp.store.clone();
let cursor_manager = evlp.cursor_manager.clone();
let surface = evlp.env.create_surface(move |dpi, surface| {
window_store.lock().unwrap().dpi_change(&surface, dpi);
surface.set_buffer_scale(dpi);
@ -165,6 +168,7 @@ impl Window {
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
need_frame_refresh,
need_refresh,
cursor_manager,
fullscreen,
})
}
@ -292,18 +296,26 @@ impl Window {
}
#[inline]
pub fn set_cursor_icon(&self, _cursor: CursorIcon) {
// TODO
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
let mut cursor_manager = self.cursor_manager.lock().unwrap();
cursor_manager.set_cursor_icon(cursor);
}
#[inline]
pub fn set_cursor_visible(&self, _visible: bool) {
// TODO: This isn't possible on Wayland yet
pub fn set_cursor_visible(&self, visible: bool) {
let mut cursor_manager = self.cursor_manager.lock().unwrap();
cursor_manager.set_cursor_visible(visible);
}
#[inline]
pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut cursor_manager = self.cursor_manager.lock().unwrap();
if grab {
cursor_manager.grab_pointer(Some(&self.surface));
} else {
cursor_manager.grab_pointer(None);
}
Ok(())
}
#[inline]
@ -440,8 +452,8 @@ impl WindowStore {
window.newsize.take(),
&mut *(window.size.lock().unwrap()),
window.new_dpi,
::std::mem::replace(&mut *window.need_refresh.lock().unwrap(), false),
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
replace(&mut *window.need_refresh.lock().unwrap(), false),
replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
window.closed,
make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m),

View file

@ -99,7 +99,8 @@ impl XConnection {
// video mode is returned to the user
monitor: None,
}
});
})
.collect();
let name_slice = slice::from_raw_parts(
(*output_info).name as *mut u8,
@ -119,7 +120,7 @@ impl XConnection {
};
(self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor, modes.collect()))
Some((name, hidpi_factor, modes))
}
pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> {
unsafe {

View file

@ -22,17 +22,46 @@ use core_video_sys::{
CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease,
};
#[derive(Derivative)]
#[derivative(Debug, Clone, PartialEq, Hash)]
#[derive(Clone)]
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate: u16,
pub(crate) monitor: MonitorHandle,
#[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")]
pub(crate) native_mode: NativeDisplayMode,
}
impl PartialEq for VideoMode {
fn eq(&self, other: &Self) -> bool {
self.size == other.size
&& self.bit_depth == other.bit_depth
&& self.refresh_rate == other.refresh_rate
&& self.monitor == other.monitor
}
}
impl Eq for VideoMode {}
impl std::hash::Hash for VideoMode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.size.hash(state);
self.bit_depth.hash(state);
self.refresh_rate.hash(state);
self.monitor.hash(state);
}
}
impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoMode")
.field("size", &self.size)
.field("bit_depth", &self.bit_depth)
.field("refresh_rate", &self.refresh_rate)
.field("monitor", &self.monitor)
.finish()
}
}
pub struct NativeDisplayMode(pub ffi::CGDisplayModeRef);
unsafe impl Send for NativeDisplayMode {}

View file

@ -402,7 +402,24 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
with_state(this, |state| {
state.with_window(|window| {
trace!("Locked shared state in `window_will_enter_fullscreen`");
window.shared_state.lock().unwrap().maximized = window.is_zoomed();
let mut shared_state = window.shared_state.lock().unwrap();
shared_state.maximized = window.is_zoomed();
match shared_state.fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user
// can't enter exclusive mode by other means (like the
// fullscreen button on the window decorations)
Some(Fullscreen::Exclusive(_)) => (),
// `window_will_enter_fullscreen` was triggered and we're already
// in fullscreen, so we must've reached here by `set_fullscreen`
// as it updates the state
Some(Fullscreen::Borderless(_)) => (),
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state!
None => {
shared_state.fullscreen = Some(Fullscreen::Borderless(window.current_monitor()))
}
}
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
})
});
@ -433,25 +450,6 @@ extern "C" fn window_will_use_fullscreen_presentation_options(
extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidEnterFullscreen:`");
with_state(this, |state| {
state.with_window(|window| {
let monitor = window.current_monitor();
trace!("Locked shared state in `window_did_enter_fullscreen`");
let mut shared_state = window.shared_state.lock().unwrap();
match shared_state.fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user
// can't enter exclusive mode by other means (like the
// fullscreen button on the window decorations)
Some(Fullscreen::Exclusive(_)) => (),
// `window_did_enter_fullscreen` was triggered and we're already
// in fullscreen, so we must've reached here by `set_fullscreen`
// as it updates the state
Some(Fullscreen::Borderless(_)) => (),
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state!
None => shared_state.fullscreen = Some(Fullscreen::Borderless(monitor)),
}
trace!("Unlocked shared state in `window_did_enter_fullscreen`");
});
state.initial_fullscreen = false;
});
trace!("Completed `windowDidEnterFullscreen:`");

View file

@ -54,13 +54,14 @@ use crate::{
},
drop_handler::FileDropHandler,
event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
monitor,
raw_input::{get_raw_input_data, get_raw_mouse_button_state},
util,
window::adjust_size,
window_state::{CursorFlags, WindowFlags, WindowState},
wrap_device_id, WindowId, DEVICE_ID,
},
window::WindowId as RootWindowId,
window::{Fullscreen, WindowId as RootWindowId},
};
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
@ -982,6 +983,51 @@ unsafe extern "system" fn public_window_callback<T>(
commctrl::DefSubclassProc(window, msg, wparam, lparam)
}
winuser::WM_WINDOWPOSCHANGING => {
let mut window_state = subclass_input.window_state.lock();
if let Some(ref mut fullscreen) = window_state.fullscreen {
let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS);
let new_rect = RECT {
left: window_pos.x,
top: window_pos.y,
right: window_pos.x + window_pos.cx,
bottom: window_pos.y + window_pos.cy,
};
let new_monitor =
winuser::MonitorFromRect(&new_rect, winuser::MONITOR_DEFAULTTONULL);
match fullscreen {
Fullscreen::Borderless(ref mut fullscreen_monitor) => {
if new_monitor != fullscreen_monitor.inner.hmonitor()
&& new_monitor != ptr::null_mut()
{
if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) {
let new_monitor_rect = new_monitor_info.rcMonitor;
window_pos.x = new_monitor_rect.left;
window_pos.y = new_monitor_rect.top;
window_pos.cx = new_monitor_rect.right - new_monitor_rect.left;
window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top;
}
*fullscreen_monitor = crate::monitor::MonitorHandle {
inner: monitor::MonitorHandle::new(new_monitor),
};
}
}
Fullscreen::Exclusive(ref video_mode) => {
let old_monitor = video_mode.video_mode.monitor.hmonitor();
if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) {
let old_monitor_rect = old_monitor_info.rcMonitor;
window_pos.x = old_monitor_rect.left;
window_pos.y = old_monitor_rect.top;
window_pos.cx = old_monitor_rect.right - old_monitor_rect.left;
window_pos.cy = old_monitor_rect.bottom - old_monitor_rect.top;
}
}
}
}
0
}
// WM_MOVE supplies client area positions, so we send Moved here instead.
winuser::WM_WINDOWPOSCHANGED => {
use crate::event::WindowEvent::Moved;

View file

@ -21,17 +21,46 @@ use crate::{
},
};
#[derive(Derivative)]
#[derivative(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Clone)]
pub struct VideoMode {
pub(crate) size: (u32, u32),
pub(crate) bit_depth: u16,
pub(crate) refresh_rate: u16,
pub(crate) monitor: MonitorHandle,
#[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")]
pub(crate) native_video_mode: wingdi::DEVMODEW,
}
impl PartialEq for VideoMode {
fn eq(&self, other: &Self) -> bool {
self.size == other.size
&& self.bit_depth == other.bit_depth
&& self.refresh_rate == other.refresh_rate
&& self.monitor == other.monitor
}
}
impl Eq for VideoMode {}
impl std::hash::Hash for VideoMode {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.size.hash(state);
self.bit_depth.hash(state);
self.refresh_rate.hash(state);
self.monitor.hash(state);
}
}
impl std::fmt::Debug for VideoMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VideoMode")
.field("size", &self.size)
.field("bit_depth", &self.bit_depth)
.field("refresh_rate", &self.refresh_rate)
.field("monitor", &self.monitor)
.finish()
}
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize {
self.size.into()

View file

@ -14,7 +14,7 @@ use std::{
use winapi::{
ctypes::c_int,
shared::{
minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM},
minwindef::{DWORD, HINSTANCE, LPARAM, UINT, WORD, WPARAM},
windef::{HWND, POINT, RECT},
},
um::{
@ -358,10 +358,16 @@ impl Window {
self.window.0
}
#[inline]
pub fn hinstance(&self) -> HINSTANCE {
unsafe { winuser::GetWindowLongW(self.hwnd(), winuser::GWL_HINSTANCE) as *mut _ }
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let handle = WindowsHandle {
hwnd: self.window.0 as *mut _,
hinstance: self.hinstance() as *mut _,
..WindowsHandle::empty()
};
RawWindowHandle::Windows(handle)

View file

@ -5,7 +5,7 @@ use crate::{
dpi::{LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError},
event_loop::EventLoopWindowTarget,
monitor::{AvailableMonitorsIter, MonitorHandle, VideoMode},
monitor::{MonitorHandle, VideoMode},
platform_impl,
};
@ -674,6 +674,8 @@ impl Window {
///
/// - **macOS:** This presently merely locks the cursor in a fixed location, which looks visually
/// awkward.
/// - **Wayland:** This presently merely locks the cursor in a fixed location, which looks visually
/// awkward.
/// - **Android:** Has no effect.
/// - **iOS:** Always returns an Err.
/// - **Web:** Has no effect.
@ -690,6 +692,7 @@ impl Window {
///
/// - **Windows:** The cursor is only hidden within the confines of the window.
/// - **X11:** The cursor is only hidden within the confines of the window.
/// - **Wayland:** The cursor is only hidden within the confines of the window.
/// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
/// outside of the window.
/// - **iOS:** Has no effect.
@ -720,11 +723,11 @@ impl Window {
///
/// **iOS:** Can only be called on the main thread.
#[inline]
pub fn available_monitors(&self) -> AvailableMonitorsIter {
let data = self.window.available_monitors();
AvailableMonitorsIter {
data: data.into_iter(),
}
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
self.window
.available_monitors()
.into_iter()
.map(|inner| MonitorHandle { inner })
}
/// Returns the primary monitor of the system.