From f4f50c9e4ae9007fcba4555d212be2146440f6b8 Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Thu, 10 Oct 2013 14:22:08 -0500 Subject: [PATCH] winemac: Implement support for Cocoa-style full-screen mode. Based in large part on a patch submitted by Kevin Eaves. --- dlls/winemac.drv/cocoa_app.m | 5 + dlls/winemac.drv/cocoa_window.h | 5 + dlls/winemac.drv/cocoa_window.m | 169 ++++++++++++++++++++++++++++---- dlls/winemac.drv/event.c | 2 +- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/macdrv_cocoa.h | 3 +- dlls/winemac.drv/window.c | 9 +- 7 files changed, 171 insertions(+), 24 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index d0cb18d4850..6e743272930 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -239,6 +239,11 @@ - (void) transformProcessToForeground submenu = [[[NSMenu alloc] initWithTitle:@"Window"] autorelease]; [submenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@""]; [submenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; + if ([NSWindow instancesRespondToSelector:@selector(toggleFullScreen:)]) + { + item = [submenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; + [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask | NSControlKeyMask]; + } [submenu addItem:[NSMenuItem separatorItem]]; [submenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; item = [[[NSMenuItem alloc] init] autorelease]; diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 210299ee6e1..d9130ac09dd 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -58,6 +58,11 @@ @interface WineWindow : NSPanel NSSize savedContentMinSize; NSSize savedContentMaxSize; + BOOL enteringFullScreen; + BOOL exitingFullScreen; + NSRect nonFullscreenFrame; + NSTimeInterval enteredFullScreenTime; + BOOL ignore_windowDeminiaturize; BOOL fakingClose; } diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 2bb9aef37f1..240812d16cc 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -28,6 +28,20 @@ #import "cocoa_opengl.h" +#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +enum { + NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7, + NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8, + NSWindowFullScreenButton = 7, + NSFullScreenWindowMask = 1 << 14, +}; + +@interface NSWindow (WineFullScreenExtensions) + - (void) toggleFullScreen:(id)sender; +@end +#endif + + /* Additional Mac virtual keycode, to complement those in Carbon's . */ enum { kVK_RightCommand = 0x36, /* Invented for Wine; was unused */ @@ -583,6 +597,39 @@ - (void) adjustFeaturesForState [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled]; if (style & NSResizableWindowMask) [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled]; + if ([self respondsToSelector:@selector(toggleFullScreen:)]) + { + if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary) + [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled]; + } + } + + - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior + { + if ([self respondsToSelector:@selector(toggleFullScreen:)]) + { + NSUInteger style = [self styleMask]; + + if (behavior & NSWindowCollectionBehaviorParticipatesInCycle && + style & NSResizableWindowMask && !(style & NSUtilityWindowMask)) + { + behavior |= NSWindowCollectionBehaviorFullScreenPrimary; + behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary; + } + else + { + behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; + behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; + if (style & NSFullScreenWindowMask) + [self toggleFullScreen:nil]; + } + } + + if (behavior != [self collectionBehavior]) + { + [self setCollectionBehavior:behavior]; + [self adjustFeaturesForState]; + } } - (void) setWindowFeatures:(const struct macdrv_window_features*)wf @@ -606,6 +653,7 @@ - (void) setWindowFeatures:(const struct macdrv_window_features*)wf [self setStyleMask:newStyle ^ NSClosableWindowMask]; } [self setStyleMask:newStyle]; + [self adjustFullScreenBehavior:[self collectionBehavior]]; } [self adjustFeaturesForState]; @@ -648,6 +696,19 @@ - (NSInteger) minimumLevelForActive:(BOOL)active return level; } + - (void) postDidUnminimizeEvent + { + macdrv_event* event; + + /* Coalesce events by discarding any previous ones still in the queue. */ + [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE) + forWindow:self]; + + event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self); + [queue postEvent:event]; + macdrv_release_event(event); + } + - (void) setMacDrvState:(const struct macdrv_window_state*)state { NSWindowCollectionBehavior behavior; @@ -703,15 +764,25 @@ - (void) setMacDrvState:(const struct macdrv_window_state*)state if ([self isOrderedIn]) [NSApp addWindowsItem:self title:[self title] filename:NO]; } - [self setCollectionBehavior:behavior]; + [self adjustFullScreenBehavior:behavior]; if (state->minimized_valid) { + BOOL discardUnminimize = TRUE; + pendingMinimize = FALSE; if (state->minimized && ![self isMiniaturized]) { if ([self isVisible]) - [super miniaturize:nil]; + { + if ([self styleMask] & NSFullScreenWindowMask) + { + [self postDidUnminimizeEvent]; + discardUnminimize = FALSE; + } + else + [super miniaturize:nil]; + } else pendingMinimize = TRUE; } @@ -721,9 +792,12 @@ - (void) setMacDrvState:(const struct macdrv_window_state*)state [self deminiaturize:nil]; } - /* Whatever events regarding minimization might have been in the queue are now stale. */ - [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE) - forWindow:self]; + if (discardUnminimize) + { + /* Whatever events regarding minimization might have been in the queue are now stale. */ + [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE) + forWindow:self]; + } } } @@ -1076,7 +1150,7 @@ - (void) doOrderOut - (void) updateFullscreen { NSRect contentRect = [self contentRectForFrameRect:[self frame]]; - BOOL nowFullscreen = (screen_covered_by_rect(contentRect, [NSScreen screens]) != nil); + BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]); if (nowFullscreen != fullscreen) { @@ -1130,6 +1204,10 @@ - (BOOL) setFrameIfOnScreen:(NSRect)contentRect [self updateColorSpace]; } + if (!enteringFullScreen && + [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0) + nonFullscreenFrame = frame; + [self updateFullscreen]; if (on_screen) @@ -1345,6 +1423,8 @@ - (BOOL) validateMenuItem:(NSMenuItem *)menuItem if ([menuItem action] == @selector(makeKeyAndOrderFront:)) ret = [self isKeyWindow] || (!self.disabled && !self.noActivate); + if ([menuItem action] == @selector(toggleFullScreen:) && self.disabled) + ret = NO; return ret; } @@ -1380,6 +1460,12 @@ - (void) miniaturize:(id)sender macdrv_release_event(event); } + - (void) toggleFullScreen:(id)sender + { + if (!self.disabled) + [super toggleFullScreen:sender]; + } + // We normally use the generic/calibrated RGB color space for the window, // rather than the device color space, to avoid expensive color conversion // which slows down drawing. However, for windows displaying OpenGL, having @@ -1483,6 +1569,25 @@ - (void) flagsChanged:(NSEvent *)theEvent /* * ---------- NSWindowDelegate methods ---------- */ + - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize + { + macdrv_query* query; + NSSize size; + + query = macdrv_create_query(); + query->type = QUERY_MIN_MAX_INFO; + query->window = (macdrv_window)[self retain]; + [self.queue query:query timeout:0.5]; + macdrv_release_query(query); + + size = [self contentMaxSize]; + if (proposedSize.width < size.width) + size.width = proposedSize.width; + if (proposedSize.height < size.height) + size.height = proposedSize.height; + return size; + } + - (void)windowDidBecomeKey:(NSNotification *)notification { WineApplicationController* controller = [WineApplicationController sharedController]; @@ -1500,18 +1605,7 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification WineApplicationController* controller = [WineApplicationController sharedController]; if (!ignore_windowDeminiaturize) - { - macdrv_event* event; - - /* Coalesce events by discarding any previous ones still in the queue. */ - [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE) - forWindow:self]; - - event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self); - [queue postEvent:event]; - macdrv_release_event(event); - } - + [self postDidUnminimizeEvent]; ignore_windowDeminiaturize = FALSE; [self becameEligibleParentOrChild]; @@ -1546,6 +1640,31 @@ - (void) windowDidEndLiveResize:(NSNotification *)notification self.liveResizeDisplayTimer = nil; } + - (void) windowDidEnterFullScreen:(NSNotification*)notification + { + enteringFullScreen = FALSE; + enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime]; + } + + - (void) windowDidExitFullScreen:(NSNotification*)notification + { + exitingFullScreen = FALSE; + [self setFrame:nonFullscreenFrame display:YES animate:NO]; + [self windowDidResize:nil]; + } + + - (void) windowDidFailToEnterFullScreen:(NSWindow*)window + { + enteringFullScreen = FALSE; + enteredFullScreenTime = 0; + } + + - (void) windowDidFailToExitFullScreen:(NSWindow*)window + { + exitingFullScreen = FALSE; + [self windowDidResize:nil]; + } + - (void)windowDidMiniaturize:(NSNotification *)notification { if (fullscreen && [self isOnActiveSpace]) @@ -1573,6 +1692,8 @@ - (void)windowDidResize:(NSNotification *)notification macdrv_event* event; NSRect frame = [self contentRectForFrameRect:[self frame]]; + if (exitingFullScreen) return; + if (self.disabled) { [self setContentMinSize:frame.size]; @@ -1587,6 +1708,7 @@ - (void)windowDidResize:(NSNotification *)notification event = macdrv_create_event(WINDOW_FRAME_CHANGED, self); event->window_frame_changed.frame = NSRectToCGRect(frame); + event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0; [queue postEvent:event]; macdrv_release_event(event); @@ -1621,6 +1743,17 @@ - (void) windowWillClose:(NSNotification*)notification [latentChildWindows removeAllObjects]; } + - (void) windowWillEnterFullScreen:(NSNotification*)notification + { + enteringFullScreen = TRUE; + nonFullscreenFrame = [self frame]; + } + + - (void) windowWillExitFullScreen:(NSNotification*)notification + { + exitingFullScreen = TRUE; + } + - (void)windowWillMiniaturize:(NSNotification *)notification { [self becameIneligibleParentOrChild]; diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index 2b7adb01880..5803ac75979 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -246,7 +246,7 @@ void macdrv_handle_event(const macdrv_event *event) macdrv_window_did_unminimize(hwnd); break; case WINDOW_FRAME_CHANGED: - macdrv_window_frame_changed(hwnd, event->window_frame_changed.frame); + macdrv_window_frame_changed(hwnd, event); break; case WINDOW_GOT_FOCUS: macdrv_window_got_focus(hwnd, event); diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 3c56c355bc5..860d29df390 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -155,7 +155,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect) extern void macdrv_handle_event(const macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_window_close_requested(HWND hwnd) DECLSPEC_HIDDEN; -extern void macdrv_window_frame_changed(HWND hwnd, CGRect frame) DECLSPEC_HIDDEN; +extern void macdrv_window_frame_changed(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_window_got_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_window_lost_focus(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN; extern void macdrv_app_deactivated(void) DECLSPEC_HIDDEN; diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 1c2d140feac..f714cc56975 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -273,7 +273,8 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, macdrv_status_item item; } status_item_mouse_move; struct { - CGRect frame; + CGRect frame; + int fullscreen; } window_frame_changed; struct { unsigned long serial; diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index b49e347600e..007d5b06f40 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -1654,7 +1654,7 @@ void macdrv_window_close_requested(HWND hwnd) * * Handler for WINDOW_FRAME_CHANGED events. */ -void macdrv_window_frame_changed(HWND hwnd, CGRect frame) +void macdrv_window_frame_changed(HWND hwnd, const macdrv_event *event) { struct macdrv_win_data *data; RECT rect; @@ -1674,9 +1674,10 @@ void macdrv_window_frame_changed(HWND hwnd, CGRect frame) parent = GetAncestor(hwnd, GA_PARENT); - TRACE("win %p/%p new Cocoa frame %s\n", hwnd, data->cocoa_window, wine_dbgstr_cgrect(frame)); + TRACE("win %p/%p new Cocoa frame %s\n", hwnd, data->cocoa_window, + wine_dbgstr_cgrect(event->window_frame_changed.frame)); - rect = rect_from_cgrect(frame); + rect = rect_from_cgrect(event->window_frame_changed.frame); macdrv_mac_to_window_rect(data, &rect); MapWindowPoints(0, parent, (POINT *)&rect, 2); @@ -1699,6 +1700,8 @@ void macdrv_window_frame_changed(HWND hwnd, CGRect frame) release_win_data(data); + if (event->window_frame_changed.fullscreen) + flags |= SWP_NOSENDCHANGING; if (!(flags & SWP_NOSIZE) || !(flags & SWP_NOMOVE)) SetWindowPos(hwnd, 0, rect.left, rect.top, width, height, flags); }