From c1377dd4d3a10adf8a6b5dc1e53bc030fec3d4a4 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 14 Apr 2018 18:48:44 +0800 Subject: [PATCH 01/11] Added helper function for make monitor from display. --- src/platform/macos/monitor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platform/macos/monitor.rs b/src/platform/macos/monitor.rs index ea37e7d879..1fe7c05fdd 100644 --- a/src/platform/macos/monitor.rs +++ b/src/platform/macos/monitor.rs @@ -25,6 +25,11 @@ impl EventsLoop { let id = MonitorId(CGDisplay::main().id); id } + + pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId { + let id = MonitorId(id); + id + } } impl MonitorId { From 5dc44aa5026f672efc8f210a72a638bd4d0a5760 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 14 Apr 2018 19:19:09 +0800 Subject: [PATCH 02/11] Implement get_current_monitor for macos --- src/platform/macos/window.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 2d6f31ae48..49b4bdcc6f 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -12,8 +12,9 @@ use objc::declare::ClassDecl; use cocoa; use cocoa::base::{id, nil}; -use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString}; -use cocoa::appkit::{self, NSApplication, NSColor, NSView, NSWindow, NSWindowStyleMask, NSWindowButton}; +use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString}; +use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton, + NSWindowStyleMask}; use core_graphics::display::CGDisplay; @@ -22,7 +23,7 @@ use std::ops::Deref; use std::os::raw::c_void; use std::sync::Weak; -use super::events_loop::Shared; +use super::events_loop::{EventsLoop,Shared}; use window::MonitorId as RootMonitorId; @@ -512,7 +513,7 @@ impl Window2 { fn create_view(window: id) -> Option { unsafe { - let view = IdRef::new(NSView::alloc(nil).init()); + let view = IdRef::new(NSView::init(NSView::alloc(nil))); view.non_nil().map(|view| { view.setWantsBestResolutionOpenGLSurface_(YES); window.setContentView_(*view); @@ -717,7 +718,18 @@ impl Window2 { #[inline] pub fn get_current_monitor(&self) -> RootMonitorId { - unimplemented!() + unsafe { + let screen = NSScreen::mainScreen(nil); + let desc = NSScreen::deviceDescription(screen); + let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber")); + + let value = NSDictionary::valueForKey_(desc, *key); + let display_id = msg_send![value,unsignedIntegerValue]; + + RootMonitorId { + inner : EventsLoop::make_monitor_from_display(display_id) + } + } } } From a317ecc81ea5a4a45ef57d6548ce89c2e10b4082 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 14 Apr 2018 20:38:48 +0800 Subject: [PATCH 03/11] Implemented with_fullscreen and set_fullscreen for macos --- src/platform/macos/monitor.rs | 2 +- src/platform/macos/window.rs | 147 ++++++++++++++++++++++++++-------- 2 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/platform/macos/monitor.rs b/src/platform/macos/monitor.rs index 1fe7c05fdd..b01ca75d0a 100644 --- a/src/platform/macos/monitor.rs +++ b/src/platform/macos/monitor.rs @@ -6,7 +6,7 @@ use std::collections::VecDeque; use super::EventsLoop; use super::window::IdRef; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct MonitorId(CGDirectDisplayID); impl EventsLoop { diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 49b4bdcc6f..34dbf82c0f 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -33,6 +33,8 @@ pub struct Id(pub usize); struct DelegateState { view: IdRef, window: IdRef, + win_attribs: WindowAttributes, + handle_with_fullscreen: bool, shared: Weak, } @@ -195,6 +197,57 @@ impl WindowDelegate { } } + /// Invoked when entered fullscreen + extern fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id){ + unsafe { + let state: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state as *mut DelegateState); + state.win_attribs.fullscreen = Some(get_current_monitor()); + + state.handle_with_fullscreen = false; + } + } + + /// Invoked when exited fullscreen + extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id){ + unsafe { + let state: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state as *mut DelegateState); + state.win_attribs.fullscreen = None; + } + } + + /// Invoked when fail to enter fullscreen + /// + /// When this window launch from a fullscreen app (e.g. launch from VS Code + /// terminal), it creates a new virtual destkop and d an transition + /// animation. This animation takes one second and cannot be disable without + /// elevated privileges. In this animation time, all toggleFullscreen events + /// will be failed. In this implementation, we will try again by using + /// performSelector:withObject:afterDelay: until window_did_enter_fullscreen. + /// It should be fine as we only do this at initialzation (i.e with_fullscreen + /// was set). + /// + /// From Apple doc: + /// In some cases, the transition to enter full-screen mode can fail, + /// due to being in the midst of handling some other animation or user gesture. + /// This method indicates that there was an error, and you should clean up any + /// work you may have done to prepare to enter full-screen mode. + extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state as *mut DelegateState); + + if state.handle_with_fullscreen { + let _: () = msg_send![*state.window, + performSelector:sel!(toggleFullScreen:) + withObject:nil + afterDelay: 0.01 + ]; + } + } + } + static mut DELEGATE_CLASS: *const Class = 0 as *const Class; static INIT: std::sync::Once = std::sync::ONCE_INIT; @@ -229,6 +282,14 @@ impl WindowDelegate { decl.add_method(sel!(draggingExited:), dragging_exited as extern fn(&Object, Sel, id)); + // callbacks for fullscreen events + decl.add_method(sel!(windowDidEnterFullScreen:), + window_did_enter_fullscreen as extern fn(&Object, Sel, id)); + decl.add_method(sel!(windowDidExitFullScreen:), + window_did_exit_fullscreen as extern fn(&Object, Sel, id)); + decl.add_method(sel!(windowDidFailToEnterFullScreen:), + window_did_fail_to_enter_fullscreen as extern fn(&Object, Sel, id)); + // Store internal state as user data decl.add_ivar::<*mut c_void>("winitState"); @@ -284,6 +345,19 @@ pub struct Window2 { unsafe impl Send for Window2 {} unsafe impl Sync for Window2 {} +unsafe fn get_current_monitor() -> RootMonitorId { + let screen = NSScreen::mainScreen(nil); + let desc = NSScreen::deviceDescription(screen); + let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber")); + + let value = NSDictionary::valueForKey_(desc, *key); + let display_id = msg_send![value, unsignedIntegerValue]; + + RootMonitorId { + inner: EventsLoop::make_monitor_from_display(display_id), + } +} + impl Drop for Window2 { fn drop(&mut self) { // Remove this window from the `EventLoop`s list of windows. @@ -354,6 +428,10 @@ impl Window2 { window.makeKeyWindow(); } + if win_attribs.fullscreen.is_some() { + window.toggleFullScreen_(nil); + } + if let Some((width, height)) = win_attribs.min_dimensions { nswindow_set_min_dimensions(window.0, width.into(), height.into()); } @@ -371,6 +449,8 @@ impl Window2 { let ds = DelegateState { view: view.clone(), window: window.clone(), + win_attribs: win_attribs.clone(), + handle_with_fullscreen: win_attribs.fullscreen.is_some(), shared: shared, }; @@ -420,19 +500,11 @@ impl Window2 { } }; - let masks = if screen.is_some() { - // Fullscreen window - NSWindowStyleMask::NSBorderlessWindowMask | - NSWindowStyleMask::NSResizableWindowMask | - NSWindowStyleMask::NSTitledWindowMask - } else if !attrs.decorations { - // Window2 without a titlebar - NSWindowStyleMask::NSBorderlessWindowMask - } else if pl_attrs.titlebar_hidden { + let masks = if pl_attrs.titlebar_hidden { NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask - } else if !pl_attrs.titlebar_transparent { - // Window2 with a titlebar + } else if pl_attrs.titlebar_transparent { + // Window2 with a transparent titlebar and regular content view NSWindowStyleMask::NSClosableWindowMask | NSWindowStyleMask::NSMiniaturizableWindowMask | NSWindowStyleMask::NSResizableWindowMask | @@ -445,11 +517,16 @@ impl Window2 { NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSFullSizeContentViewWindowMask } else { - // Window2 with a transparent titlebar and regular content view - NSWindowStyleMask::NSClosableWindowMask | - NSWindowStyleMask::NSMiniaturizableWindowMask | - NSWindowStyleMask::NSResizableWindowMask | - NSWindowStyleMask::NSTitledWindowMask + if !attrs.decorations { + // Window2 without a titlebar + NSWindowStyleMask::NSBorderlessWindowMask + } else { + // Window2 with a titlebar + NSWindowStyleMask::NSClosableWindowMask | + NSWindowStyleMask::NSMiniaturizableWindowMask | + NSWindowStyleMask::NSResizableWindowMask | + NSWindowStyleMask::NSTitledWindowMask + } }; let winit_window = Class::get("WinitWindow").unwrap_or_else(|| { @@ -500,12 +577,7 @@ impl Window2 { window.setTitlebarAppearsTransparent_(YES); } - if screen.is_some() { - window.setLevel_(appkit::NSMainMenuWindowLevel as i64 + 1); - } - else { - window.center(); - } + window.center(); window }) } @@ -707,8 +779,24 @@ impl Window2 { } #[inline] - pub fn set_fullscreen(&self, _monitor: Option) { - unimplemented!() + /// TODO: Right now set_fullscreen do not work on switching monitors + /// in fullscreen mode + pub fn set_fullscreen(&self, monitor: Option) { + let state = &self.delegate.state; + let current = state.win_attribs.fullscreen.clone(); + match (current, monitor) { + (None, None) => { + return; + } + (Some(_), Some(_)) => { + return; + } + _ => (), + } + + unsafe { + self.window.toggleFullScreen_(nil); + } } #[inline] @@ -719,16 +807,7 @@ impl Window2 { #[inline] pub fn get_current_monitor(&self) -> RootMonitorId { unsafe { - let screen = NSScreen::mainScreen(nil); - let desc = NSScreen::deviceDescription(screen); - let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber")); - - let value = NSDictionary::valueForKey_(desc, *key); - let display_id = msg_send![value,unsignedIntegerValue]; - - RootMonitorId { - inner : EventsLoop::make_monitor_from_display(display_id) - } + self::get_current_monitor() } } } From 18380d3afae33e881e7bff7fd175b06cbf711172 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 14 Apr 2018 21:37:53 +0800 Subject: [PATCH 04/11] Implemented set_decorations for macos --- src/platform/macos/window.rs | 62 ++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 34dbf82c0f..11fb3f7b14 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -22,6 +22,7 @@ use std; use std::ops::Deref; use std::os::raw::c_void; use std::sync::Weak; +use std::cell::RefCell; use super::events_loop::{EventsLoop,Shared}; @@ -33,9 +34,10 @@ pub struct Id(pub usize); struct DelegateState { view: IdRef, window: IdRef, - win_attribs: WindowAttributes, - handle_with_fullscreen: bool, shared: Weak, + + win_attribs: RefCell, + handle_with_fullscreen: bool, } pub struct WindowDelegate { @@ -202,7 +204,7 @@ impl WindowDelegate { unsafe { let state: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state as *mut DelegateState); - state.win_attribs.fullscreen = Some(get_current_monitor()); + state.win_attribs.borrow_mut().fullscreen = Some(get_current_monitor()); state.handle_with_fullscreen = false; } @@ -213,9 +215,14 @@ impl WindowDelegate { unsafe { let state: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state as *mut DelegateState); - state.win_attribs.fullscreen = None; + let mut win_attribs = state.win_attribs.borrow_mut(); + + win_attribs.fullscreen = None; + if !win_attribs.decorations { + state.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); } } + } /// Invoked when fail to enter fullscreen /// @@ -449,7 +456,7 @@ impl Window2 { let ds = DelegateState { view: view.clone(), window: window.clone(), - win_attribs: win_attribs.clone(), + win_attribs: RefCell::new(win_attribs.clone()), handle_with_fullscreen: win_attribs.fullscreen.is_some(), shared: shared, }; @@ -517,7 +524,7 @@ impl Window2 { NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSFullSizeContentViewWindowMask } else { - if !attrs.decorations { + if !attrs.decorations && !screen.is_some() { // Window2 without a titlebar NSWindowStyleMask::NSBorderlessWindowMask } else { @@ -783,8 +790,10 @@ impl Window2 { /// in fullscreen mode pub fn set_fullscreen(&self, monitor: Option) { let state = &self.delegate.state; - let current = state.win_attribs.fullscreen.clone(); - match (current, monitor) { + let win_attribs = state.win_attribs.borrow_mut(); + + let current = win_attribs.fullscreen.clone(); + match (current.clone(), monitor.clone()) { (None, None) => { return; } @@ -795,13 +804,46 @@ impl Window2 { } unsafe { + // Because toggleFullScreen will not work if the StyleMask is none, + // We set a normal style to it temporary. + // It will clean up at window_did_exit_fullscreen. + if current.is_none() && !win_attribs.decorations { + state.window.setStyleMask_(NSWindowStyleMask::NSTitledWindowMask|NSWindowStyleMask::NSResizableWindowMask); + } + self.window.toggleFullScreen_(nil); } } #[inline] - pub fn set_decorations(&self, _decorations: bool) { - unimplemented!() + pub fn set_decorations(&self, decorations: bool) { + let state = &self.delegate.state; + let mut win_attribs = state.win_attribs.borrow_mut(); + + if win_attribs.decorations == decorations { + return; + } + + win_attribs.decorations = decorations; + + // Skip modifiy if we are in fullscreen mode, + // window_did_exit_fullscreen will handle it + if win_attribs.fullscreen.is_some() { + return; + } + + unsafe { + let new_mask = if decorations { + NSWindowStyleMask::NSClosableWindowMask + | NSWindowStyleMask::NSMiniaturizableWindowMask + | NSWindowStyleMask::NSResizableWindowMask + | NSWindowStyleMask::NSTitledWindowMask + } else { + NSWindowStyleMask::NSBorderlessWindowMask + }; + + state.window.setStyleMask_(new_mask); + } } #[inline] From e6b5d8e97f4ef556a309daa6845fd6806dcb511c Mon Sep 17 00:00:00 2001 From: user Date: Sun, 15 Apr 2018 00:40:27 +0800 Subject: [PATCH 05/11] Implement set_maximized and with_maximized for macos --- src/platform/macos/window.rs | 98 ++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 11fb3f7b14..be15e5a725 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -22,7 +22,7 @@ use std; use std::ops::Deref; use std::os::raw::c_void; use std::sync::Weak; -use std::cell::RefCell; +use std::cell::{Cell,RefCell}; use super::events_loop::{EventsLoop,Shared}; @@ -37,9 +37,77 @@ struct DelegateState { shared: Weak, win_attribs: RefCell, + standard_frame: Cell>, + handle_with_fullscreen: bool, } +impl DelegateState { + fn is_zoomed(&self) -> bool { + let win_attribs = self.win_attribs.borrow(); + + unsafe { + // Because isZoomed do not work in Borderless mode, we set it + // resizable temporality + if !win_attribs.decorations { + self.window.setStyleMask_(NSWindowStyleMask::NSResizableWindowMask); + } + + let is_zoomed: BOOL = msg_send![*self.window, isZoomed]; + + // Roll the the style + if !win_attribs.decorations { + self.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); + } + + is_zoomed != 0 + } + } + + fn perform_maximized(&self, maximized: bool) { + let is_zoomed = self.is_zoomed(); + + if is_zoomed == maximized { + return; + } + + // Save the standard frame sized if it is not zoomed + if !is_zoomed { + unsafe { + self.standard_frame.set(Some(NSWindow::frame(*self.window))); + } + } + + let mut win_attribs = self.win_attribs.borrow_mut(); + win_attribs.maximized = maximized; + + if win_attribs.fullscreen.is_some() { + // Handle it in window_did_exit_fullscreen + return; + } else if win_attribs.decorations { + // Just use the native zoom if not borderless + unsafe { + self.window.zoom_(nil); + } + } else { + // if it is borderless, we set the frame directly + unsafe { + let new_rect = if maximized { + let screen = NSScreen::mainScreen(nil); + NSScreen::visibleFrame(screen) + } else { + self.standard_frame.get().unwrap_or(NSRect::new( + NSPoint::new(50.0, 50.0), + NSSize::new(800.0, 600.0), + )) + }; + + self.window.setFrame_display_(new_rect, 0); + } + } + } +} + pub struct WindowDelegate { state: Box, _this: IdRef, @@ -212,16 +280,23 @@ impl WindowDelegate { /// Invoked when exited fullscreen extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id){ - unsafe { + let state = unsafe { let state: *mut c_void = *this.get_ivar("winitState"); - let state = &mut *(state as *mut DelegateState); + &mut *(state as *mut DelegateState) + }; + + let maximized = unsafe { let mut win_attribs = state.win_attribs.borrow_mut(); win_attribs.fullscreen = None; if !win_attribs.decorations { state.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); - } - } + } + + win_attribs.maximized + }; + + state.perform_maximized(maximized); } /// Invoked when fail to enter fullscreen @@ -249,7 +324,7 @@ impl WindowDelegate { let _: () = msg_send![*state.window, performSelector:sel!(toggleFullScreen:) withObject:nil - afterDelay: 0.01 + afterDelay: 0.5 ]; } } @@ -296,7 +371,7 @@ impl WindowDelegate { window_did_exit_fullscreen as extern fn(&Object, Sel, id)); decl.add_method(sel!(windowDidFailToEnterFullScreen:), window_did_fail_to_enter_fullscreen as extern fn(&Object, Sel, id)); - + // Store internal state as user data decl.add_ivar::<*mut c_void>("winitState"); @@ -457,6 +532,7 @@ impl Window2 { view: view.clone(), window: window.clone(), win_attribs: RefCell::new(win_attribs.clone()), + standard_frame: Cell::new(None), handle_with_fullscreen: win_attribs.fullscreen.is_some(), shared: shared, }; @@ -467,6 +543,10 @@ impl Window2 { delegate: WindowDelegate::new(ds), }; + if win_attribs.maximized { + window.delegate.state.perform_maximized(win_attribs.maximized); + } + Ok(window) } @@ -781,8 +861,8 @@ impl Window2 { } #[inline] - pub fn set_maximized(&self, _maximized: bool) { - unimplemented!() + pub fn set_maximized(&self, maximized: bool) { + self.delegate.state.perform_maximized(maximized) } #[inline] From e50ea131abf8bbe69345ffcc9ef7aa48b0118ac3 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 15 Apr 2018 00:55:51 +0800 Subject: [PATCH 06/11] Changed fullscreen example fullscreen keypress from F11 to F --- examples/fullscreen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 481ba223c2..067b3bae35 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -51,7 +51,7 @@ fn main() { .. } => match (virtual_code, state) { (winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break, - (winit::VirtualKeyCode::F11, winit::ElementState::Pressed) => { + (winit::VirtualKeyCode::F, winit::ElementState::Pressed) => { is_fullscreen = !is_fullscreen; if !is_fullscreen { window.set_fullscreen(None); From 8f92a82255b732f17e21d7b0c59e23f4bbd39055 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 15 Apr 2018 01:01:43 +0800 Subject: [PATCH 07/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3705c5793..8fd973ed94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS. - Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows. - On Windows, `WindowBuilder::with_dimensions` no longer changing monitor display resolution. - Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now. From dc72e76a65f4462dea82c2b36de1a6f897dcb025 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 15 Apr 2018 11:36:03 +0800 Subject: [PATCH 08/11] Add and fixed some comments --- src/platform/macos/window.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index be15e5a725..4584058e57 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -55,7 +55,7 @@ impl DelegateState { let is_zoomed: BOOL = msg_send![*self.window, isZoomed]; - // Roll the the style + // Roll back temp styles if !win_attribs.decorations { self.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); } @@ -302,8 +302,8 @@ impl WindowDelegate { /// Invoked when fail to enter fullscreen /// /// When this window launch from a fullscreen app (e.g. launch from VS Code - /// terminal), it creates a new virtual destkop and d an transition - /// animation. This animation takes one second and cannot be disable without + /// terminal), it creates a new virtual destkop and a transition animation. + /// This animation takes one second and cannot be disable without /// elevated privileges. In this animation time, all toggleFullscreen events /// will be failed. In this implementation, we will try again by using /// performSelector:withObject:afterDelay: until window_did_enter_fullscreen. @@ -427,6 +427,7 @@ pub struct Window2 { unsafe impl Send for Window2 {} unsafe impl Sync for Window2 {} +/// Helpper funciton to convert NSScreen::mainScreen to MonitorId unsafe fn get_current_monitor() -> RootMonitorId { let screen = NSScreen::mainScreen(nil); let desc = NSScreen::deviceDescription(screen); From 88428752e4c35876383192162bfa6f6ebcef80d4 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 17 Apr 2018 21:17:04 +0800 Subject: [PATCH 09/11] Reformat and add more comments --- src/platform/macos/window.rs | 68 +++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 4584058e57..75389186be 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -11,10 +11,10 @@ use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; use objc::declare::ClassDecl; use cocoa; +use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton, + NSWindowStyleMask}; use cocoa::base::{id, nil}; use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString}; -use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton, - NSWindowStyleMask}; use core_graphics::display::CGDisplay; @@ -24,7 +24,7 @@ use std::os::raw::c_void; use std::sync::Weak; use std::cell::{Cell,RefCell}; -use super::events_loop::{EventsLoop,Shared}; +use super::events_loop::{EventsLoop, Shared}; use window::MonitorId as RootMonitorId; @@ -39,6 +39,8 @@ struct DelegateState { win_attribs: RefCell, standard_frame: Cell>, + // This is set when WindowBuilder::with_fullscreen was set, + // see comments of `window_did_fail_to_enter_fullscreen` handle_with_fullscreen: bool, } @@ -47,23 +49,41 @@ impl DelegateState { let win_attribs = self.win_attribs.borrow(); unsafe { - // Because isZoomed do not work in Borderless mode, we set it + // Because isZoomed do not work in Borderless mode, we set it // resizable temporality if !win_attribs.decorations { - self.window.setStyleMask_(NSWindowStyleMask::NSResizableWindowMask); - } + self.window + .setStyleMask_(NSWindowStyleMask::NSResizableWindowMask); + } let is_zoomed: BOOL = msg_send![*self.window, isZoomed]; // Roll back temp styles if !win_attribs.decorations { - self.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); - } + self.window + .setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); + } is_zoomed != 0 } } + fn restore_state_from_fullscreen(&mut self) { + let maximized = unsafe { + let mut win_attribs = self.win_attribs.borrow_mut(); + + win_attribs.fullscreen = None; + if !win_attribs.decorations { + self.window + .setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); + } + + win_attribs.maximized + }; + + self.perform_maximized(maximized); + } + fn perform_maximized(&self, maximized: bool) { let is_zoomed = self.is_zoomed(); @@ -285,18 +305,7 @@ impl WindowDelegate { &mut *(state as *mut DelegateState) }; - let maximized = unsafe { - let mut win_attribs = state.win_attribs.borrow_mut(); - - win_attribs.fullscreen = None; - if !win_attribs.decorations { - state.window.setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); - } - - win_attribs.maximized - }; - - state.perform_maximized(maximized); + state.restore_state_from_fullscreen(); } /// Invoked when fail to enter fullscreen @@ -315,7 +324,7 @@ impl WindowDelegate { /// due to being in the midst of handling some other animation or user gesture. /// This method indicates that there was an error, and you should clean up any /// work you may have done to prepare to enter full-screen mode. - extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { + extern fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) { unsafe { let state: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state as *mut DelegateState); @@ -327,6 +336,9 @@ impl WindowDelegate { afterDelay: 0.5 ]; } + else { + state.restore_state_from_fullscreen(); + } } } @@ -874,11 +886,14 @@ impl Window2 { let win_attribs = state.win_attribs.borrow_mut(); let current = win_attribs.fullscreen.clone(); - match (current.clone(), monitor.clone()) { - (None, None) => { + match (¤t, monitor) { + (&None, None) => { return; } - (Some(_), Some(_)) => { + (&Some(ref a), Some(ref b)) if a.inner == b.inner => { + unimplemented!(); + } + (&Some(_), Some(_)) => { return; } _ => (), @@ -889,7 +904,10 @@ impl Window2 { // We set a normal style to it temporary. // It will clean up at window_did_exit_fullscreen. if current.is_none() && !win_attribs.decorations { - state.window.setStyleMask_(NSWindowStyleMask::NSTitledWindowMask|NSWindowStyleMask::NSResizableWindowMask); + state.window.setStyleMask_( + NSWindowStyleMask::NSTitledWindowMask + | NSWindowStyleMask::NSResizableWindowMask, + ); } self.window.toggleFullScreen_(nil); From 1a808b023b8172f8b075d4f503b5aad2b7ab212a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 17 Apr 2018 23:02:02 +0800 Subject: [PATCH 10/11] Better handling window and maximized state --- src/platform/macos/window.rs | 118 +++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 41 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 75389186be..6928360f90 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -38,6 +38,7 @@ struct DelegateState { win_attribs: RefCell, standard_frame: Cell>, + save_style_mask: Cell>, // This is set when WindowBuilder::with_fullscreen was set, // see comments of `window_did_fail_to_enter_fullscreen` @@ -46,12 +47,12 @@ struct DelegateState { impl DelegateState { fn is_zoomed(&self) -> bool { - let win_attribs = self.win_attribs.borrow(); - unsafe { // Because isZoomed do not work in Borderless mode, we set it // resizable temporality - if !win_attribs.decorations { + let curr_mask = self.window.styleMask(); + + if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) { self.window .setStyleMask_(NSWindowStyleMask::NSResizableWindowMask); } @@ -59,9 +60,8 @@ impl DelegateState { let is_zoomed: BOOL = msg_send![*self.window, isZoomed]; // Roll back temp styles - if !win_attribs.decorations { - self.window - .setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); + if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) { + self.window.setStyleMask_(curr_mask); } is_zoomed != 0 @@ -73,9 +73,10 @@ impl DelegateState { let mut win_attribs = self.win_attribs.borrow_mut(); win_attribs.fullscreen = None; - if !win_attribs.decorations { - self.window - .setStyleMask_(NSWindowStyleMask::NSBorderlessWindowMask); + let save_style_opt = self.save_style_mask.take(); + + if let Some(save_style) = save_style_opt { + self.window.setStyleMask_(save_style); } win_attribs.maximized @@ -298,6 +299,17 @@ impl WindowDelegate { } } + /// Invoked when before enter fullscreen + extern fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state as *mut DelegateState); + let is_zoomed = state.is_zoomed(); + + state.win_attribs.borrow_mut().maximized = is_zoomed; + } + } + /// Invoked when exited fullscreen extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id){ let state = unsafe { @@ -379,6 +391,8 @@ impl WindowDelegate { // callbacks for fullscreen events decl.add_method(sel!(windowDidEnterFullScreen:), window_did_enter_fullscreen as extern fn(&Object, Sel, id)); + decl.add_method(sel!(windowWillEnterFullScreen:), + window_will_enter_fullscreen as extern fn(&Object, Sel, id)); decl.add_method(sel!(windowDidExitFullScreen:), window_did_exit_fullscreen as extern fn(&Object, Sel, id)); decl.add_method(sel!(windowDidFailToEnterFullScreen:), @@ -484,11 +498,11 @@ impl WindowExt for Window2 { } impl Window2 { - pub fn new(shared: Weak, - win_attribs: &WindowAttributes, - pl_attribs: &PlatformSpecificWindowBuilderAttributes) - -> Result - { + pub fn new( + shared: Weak, + win_attribs: &WindowAttributes, + pl_attribs: &PlatformSpecificWindowBuilderAttributes, + ) -> Result { unsafe { if !msg_send![cocoa::base::class("NSThread"), isMainThread] { panic!("Windows can only be created on the main thread on macOS"); @@ -517,15 +531,6 @@ impl Window2 { } app.activateIgnoringOtherApps_(YES); - if win_attribs.visible { - window.makeKeyAndOrderFront_(nil); - } else { - window.makeKeyWindow(); - } - - if win_attribs.fullscreen.is_some() { - window.toggleFullScreen_(nil); - } if let Some((width, height)) = win_attribs.min_dimensions { nswindow_set_min_dimensions(window.0, width.into(), height.into()); @@ -546,9 +551,11 @@ impl Window2 { window: window.clone(), win_attribs: RefCell::new(win_attribs.clone()), standard_frame: Cell::new(None), + save_style_mask: Cell::new(None), handle_with_fullscreen: win_attribs.fullscreen.is_some(), shared: shared, }; + ds.win_attribs.borrow_mut().fullscreen = None; let window = Window2 { view: view, @@ -556,6 +563,26 @@ impl Window2 { delegate: WindowDelegate::new(ds), }; + // Set fullscreen mode after we setup everything + if let Some(ref monitor) = win_attribs.fullscreen { + unsafe { + if monitor.inner != get_current_monitor().inner { + unimplemented!(); + } + } + window.set_fullscreen(Some(monitor.clone())); + } + + // Make key have to be after set fullscreen + // to prevent normal zie window brefly appears + unsafe { + if win_attribs.visible { + window.window.makeKeyAndOrderFront_(nil); + } else { + window.window.makeKeyWindow(); + } + } + if win_attribs.maximized { window.delegate.state.perform_maximized(win_attribs.maximized); } @@ -883,31 +910,40 @@ impl Window2 { /// in fullscreen mode pub fn set_fullscreen(&self, monitor: Option) { let state = &self.delegate.state; - let win_attribs = state.win_attribs.borrow_mut(); + let current = { + let win_attribs = state.win_attribs.borrow_mut(); - let current = win_attribs.fullscreen.clone(); - match (¤t, monitor) { - (&None, None) => { - return; - } - (&Some(ref a), Some(ref b)) if a.inner == b.inner => { - unimplemented!(); - } - (&Some(_), Some(_)) => { - return; + let current = win_attribs.fullscreen.clone(); + match (¤t, monitor) { + (&None, None) => { + return; + } + (&Some(ref a), Some(ref b)) if a.inner != b.inner => { + unimplemented!(); + } + (&Some(_), Some(_)) => { + return; + } + _ => (), } - _ => (), - } + + current + }; unsafe { // Because toggleFullScreen will not work if the StyleMask is none, // We set a normal style to it temporary. // It will clean up at window_did_exit_fullscreen. - if current.is_none() && !win_attribs.decorations { - state.window.setStyleMask_( - NSWindowStyleMask::NSTitledWindowMask - | NSWindowStyleMask::NSResizableWindowMask, - ); + if current.is_none() { + let curr_mask = state.window.styleMask(); + + if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) { + state.window.setStyleMask_( + NSWindowStyleMask::NSTitledWindowMask + | NSWindowStyleMask::NSResizableWindowMask, + ); + state.save_style_mask.set(Some(curr_mask)); + } } self.window.toggleFullScreen_(nil); From 71220a97d6226fca6f887d25d6b3c9d8f86a7306 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Wed, 18 Apr 2018 01:54:00 +0800 Subject: [PATCH 11/11] Reformat and typo fix --- src/platform/macos/window.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 6928360f90..91b09753ec 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -347,8 +347,7 @@ impl WindowDelegate { withObject:nil afterDelay: 0.5 ]; - } - else { + } else { state.restore_state_from_fullscreen(); } } @@ -574,7 +573,7 @@ impl Window2 { } // Make key have to be after set fullscreen - // to prevent normal zie window brefly appears + // to prevent normal size window brefly appears unsafe { if win_attribs.visible { window.window.makeKeyAndOrderFront_(nil);