2013-01-07 21:44:44 +01:00
|
|
|
/*
|
|
|
|
* MACDRV Cocoa window code
|
|
|
|
*
|
|
|
|
* Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
2013-02-04 00:20:18 +01:00
|
|
|
#import <Carbon/Carbon.h>
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
#import <CoreVideo/CoreVideo.h>
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
#import <Metal/Metal.h>
|
|
|
|
#import <QuartzCore/QuartzCore.h>
|
|
|
|
#endif
|
2013-02-04 00:20:18 +01:00
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
#import "cocoa_window.h"
|
|
|
|
|
|
|
|
#include "macdrv_cocoa.h"
|
|
|
|
#import "cocoa_app.h"
|
2013-01-21 07:07:58 +01:00
|
|
|
#import "cocoa_event.h"
|
2013-03-06 11:59:07 +01:00
|
|
|
#import "cocoa_opengl.h"
|
2013-01-07 21:44:44 +01:00
|
|
|
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
#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
|
|
|
|
|
|
|
|
|
2016-06-23 23:53:04 +02:00
|
|
|
#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
|
2013-02-04 00:20:18 +01:00
|
|
|
/* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
|
|
|
|
enum {
|
|
|
|
kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
|
|
|
|
};
|
2016-06-23 23:53:04 +02:00
|
|
|
#endif
|
2013-02-04 00:20:18 +01:00
|
|
|
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
|
|
|
|
{
|
|
|
|
NSUInteger style_mask;
|
|
|
|
|
|
|
|
if (wf->title_bar)
|
|
|
|
{
|
|
|
|
style_mask = NSTitledWindowMask;
|
|
|
|
if (wf->close_button) style_mask |= NSClosableWindowMask;
|
|
|
|
if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
|
2013-12-30 04:34:48 +01:00
|
|
|
if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
|
2013-01-07 21:44:44 +01:00
|
|
|
if (wf->utility) style_mask |= NSUtilityWindowMask;
|
|
|
|
}
|
|
|
|
else style_mask = NSBorderlessWindowMask;
|
|
|
|
|
|
|
|
return style_mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
|
|
|
|
{
|
|
|
|
NSScreen* screen;
|
|
|
|
for (screen in screens)
|
|
|
|
{
|
|
|
|
if (NSIntersectsRect(frame, [screen frame]))
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-18 02:28:30 +01:00
|
|
|
static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
|
|
|
|
{
|
|
|
|
for (NSScreen* screen in screens)
|
|
|
|
{
|
|
|
|
if (NSContainsRect(rect, [screen frame]))
|
|
|
|
return screen;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-04 00:20:18 +01:00
|
|
|
/* We rely on the supposedly device-dependent modifier flags to distinguish the
|
|
|
|
keys on the left side of the keyboard from those on the right. Some event
|
|
|
|
sources don't set those device-depdendent flags. If we see a device-independent
|
|
|
|
flag for a modifier without either corresponding device-dependent flag, assume
|
|
|
|
the left one. */
|
|
|
|
static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
|
|
|
|
{
|
|
|
|
if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
|
|
|
|
*modifiers |= NX_DEVICELCMDKEYMASK;
|
|
|
|
if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
|
|
|
|
*modifiers |= NX_DEVICELSHIFTKEYMASK;
|
|
|
|
if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
|
|
|
|
*modifiers |= NX_DEVICELCTLKEYMASK;
|
|
|
|
if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
|
|
|
|
*modifiers |= NX_DEVICELALTKEYMASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* As we manipulate individual bits of a modifier mask, we can end up with
|
|
|
|
inconsistent sets of flags. In particular, we might set or clear one of the
|
|
|
|
left/right-specific bits, but not the corresponding non-side-specific bit.
|
|
|
|
Fix that. If either side-specific bit is set, set the non-side-specific bit,
|
|
|
|
otherwise clear it. */
|
|
|
|
static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
|
|
|
|
{
|
|
|
|
if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
|
|
|
|
*modifiers |= NX_COMMANDMASK;
|
|
|
|
else
|
|
|
|
*modifiers &= ~NX_COMMANDMASK;
|
|
|
|
if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
|
|
|
|
*modifiers |= NX_SHIFTMASK;
|
|
|
|
else
|
|
|
|
*modifiers &= ~NX_SHIFTMASK;
|
|
|
|
if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
|
|
|
|
*modifiers |= NX_CONTROLMASK;
|
|
|
|
else
|
|
|
|
*modifiers &= ~NX_CONTROLMASK;
|
|
|
|
if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
|
|
|
|
*modifiers |= NX_ALTERNATEMASK;
|
|
|
|
else
|
|
|
|
*modifiers &= ~NX_ALTERNATEMASK;
|
|
|
|
}
|
|
|
|
|
2018-09-12 11:35:30 +02:00
|
|
|
static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
|
2013-10-09 23:30:59 +02:00
|
|
|
{
|
|
|
|
fix_device_modifiers_by_generic(&modifiers);
|
2018-09-12 11:35:30 +02:00
|
|
|
NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
|
|
|
|
NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
|
|
|
|
|
|
|
|
// The MACDRV keyboard driver translates Command keys to Alt. If the
|
|
|
|
// Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
|
|
|
|
// Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
|
|
|
|
if (modifiers & NX_DEVICELALTKEYMASK)
|
|
|
|
new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
|
|
|
|
if (modifiers & NX_DEVICERALTKEYMASK)
|
|
|
|
new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
|
|
|
|
|
|
|
|
if (modifiers & NX_DEVICELCMDKEYMASK)
|
|
|
|
new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
|
|
|
|
if (modifiers & NX_DEVICERCMDKEYMASK)
|
|
|
|
new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
|
|
|
|
|
|
|
|
fix_generic_modifiers_by_device(&new_modifiers);
|
|
|
|
return new_modifiers;
|
2013-10-09 23:30:59 +02:00
|
|
|
}
|
|
|
|
|
2013-02-04 00:20:18 +01:00
|
|
|
|
2015-10-05 22:44:28 +02:00
|
|
|
@interface NSWindow (WineAccessPrivateMethods)
|
|
|
|
- (id) _displayChanged;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
@interface WineDisplayLink : NSObject
|
|
|
|
{
|
|
|
|
CGDirectDisplayID _displayID;
|
|
|
|
CVDisplayLinkRef _link;
|
|
|
|
NSMutableSet* _windows;
|
2015-11-10 03:31:22 +01:00
|
|
|
|
|
|
|
NSTimeInterval _actualRefreshPeriod;
|
|
|
|
NSTimeInterval _nominalRefreshPeriod;
|
2017-12-08 09:54:08 +01:00
|
|
|
|
|
|
|
NSTimeInterval _lastDisplayTime;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithDisplayID:(CGDirectDisplayID)displayID;
|
|
|
|
|
|
|
|
- (void) addWindow:(WineWindow*)window;
|
|
|
|
- (void) removeWindow:(WineWindow*)window;
|
|
|
|
|
2015-11-10 03:31:22 +01:00
|
|
|
- (NSTimeInterval) refreshPeriod;
|
|
|
|
|
|
|
|
- (void) start;
|
|
|
|
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation WineDisplayLink
|
|
|
|
|
|
|
|
static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
|
|
|
|
|
|
|
|
- (id) initWithDisplayID:(CGDirectDisplayID)displayID
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
|
|
|
|
if (status == kCVReturnSuccess && !_link)
|
|
|
|
status = kCVReturnError;
|
|
|
|
if (status == kCVReturnSuccess)
|
|
|
|
status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
|
|
|
|
if (status != kCVReturnSuccess)
|
|
|
|
{
|
|
|
|
[self release];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
_displayID = displayID;
|
|
|
|
_windows = [[NSMutableSet alloc] init];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if (_link)
|
|
|
|
{
|
|
|
|
CVDisplayLinkStop(_link);
|
|
|
|
CVDisplayLinkRelease(_link);
|
|
|
|
}
|
|
|
|
[_windows release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) addWindow:(WineWindow*)window
|
|
|
|
{
|
2017-12-08 09:54:06 +01:00
|
|
|
BOOL firstWindow;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
@synchronized(self) {
|
2017-12-08 09:54:06 +01:00
|
|
|
firstWindow = !_windows.count;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[_windows addObject:window];
|
|
|
|
}
|
2017-12-08 09:54:06 +01:00
|
|
|
if (firstWindow || !CVDisplayLinkIsRunning(_link))
|
2017-12-08 09:54:08 +01:00
|
|
|
[self start];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeWindow:(WineWindow*)window
|
|
|
|
{
|
2017-12-08 09:54:06 +01:00
|
|
|
BOOL lastWindow = FALSE;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
@synchronized(self) {
|
2017-12-08 09:54:06 +01:00
|
|
|
BOOL hadWindows = _windows.count > 0;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[_windows removeObject:window];
|
2017-12-08 09:54:06 +01:00
|
|
|
if (hadWindows && !_windows.count)
|
|
|
|
lastWindow = TRUE;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
}
|
2017-12-08 09:54:06 +01:00
|
|
|
if (lastWindow && CVDisplayLinkIsRunning(_link))
|
2017-05-08 19:52:02 +02:00
|
|
|
CVDisplayLinkStop(_link);
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) fire
|
|
|
|
{
|
|
|
|
NSSet* windows;
|
|
|
|
@synchronized(self) {
|
|
|
|
windows = [_windows copy];
|
|
|
|
}
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
2015-11-10 03:31:22 +01:00
|
|
|
BOOL anyDisplayed = FALSE;
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
for (WineWindow* window in windows)
|
2015-11-10 03:31:22 +01:00
|
|
|
{
|
|
|
|
if ([window viewsNeedDisplay])
|
|
|
|
{
|
|
|
|
[window displayIfNeeded];
|
|
|
|
anyDisplayed = YES;
|
|
|
|
}
|
|
|
|
}
|
2017-12-08 09:54:08 +01:00
|
|
|
|
|
|
|
NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
|
|
|
|
if (anyDisplayed)
|
|
|
|
_lastDisplayTime = now;
|
|
|
|
else if (_lastDisplayTime + 2.0 < now)
|
2015-11-10 03:31:22 +01:00
|
|
|
CVDisplayLinkStop(_link);
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
});
|
|
|
|
[windows release];
|
|
|
|
}
|
|
|
|
|
2015-11-10 03:31:22 +01:00
|
|
|
- (NSTimeInterval) refreshPeriod
|
|
|
|
{
|
|
|
|
if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
|
|
|
|
return _actualRefreshPeriod;
|
|
|
|
|
|
|
|
if (_nominalRefreshPeriod)
|
|
|
|
return _nominalRefreshPeriod;
|
|
|
|
|
|
|
|
CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
|
|
|
|
if (time.flags & kCVTimeIsIndefinite)
|
|
|
|
return 1.0 / 60.0;
|
|
|
|
_nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
|
|
|
|
return _nominalRefreshPeriod;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) start
|
|
|
|
{
|
2017-12-08 09:54:08 +01:00
|
|
|
_lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
|
2015-11-10 03:31:22 +01:00
|
|
|
CVDisplayLinkStart(_link);
|
|
|
|
}
|
|
|
|
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
|
|
|
|
{
|
|
|
|
WineDisplayLink* link = displayLinkContext;
|
|
|
|
[link fire];
|
|
|
|
return kCVReturnSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2018-07-13 16:27:00 +02:00
|
|
|
@interface WineBaseView : NSView
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
@interface WineMetalView : WineBaseView
|
|
|
|
{
|
|
|
|
id<MTLDevice> _device;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
|
|
|
|
|
|
|
|
@end
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-07-13 16:27:00 +02:00
|
|
|
@interface WineContentView : WineBaseView <NSTextInputClient>
|
2013-03-06 11:59:07 +01:00
|
|
|
{
|
|
|
|
NSMutableArray* glContexts;
|
|
|
|
NSMutableArray* pendingGlContexts;
|
2017-05-08 22:31:48 +02:00
|
|
|
BOOL _everHadGLContext;
|
2016-05-13 01:50:39 +02:00
|
|
|
BOOL _cachedHasGLDescendant;
|
|
|
|
BOOL _cachedHasGLDescendantValid;
|
2013-11-27 22:33:19 +01:00
|
|
|
BOOL clearedGlSurface;
|
2013-04-22 04:32:26 +02:00
|
|
|
|
|
|
|
NSMutableAttributedString* markedText;
|
|
|
|
NSRange markedTextSelection;
|
2016-05-05 20:53:36 +02:00
|
|
|
|
|
|
|
int backingSize[2];
|
2018-07-13 16:27:44 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
WineMetalView *_metalView;
|
|
|
|
#endif
|
2013-03-06 11:59:07 +01:00
|
|
|
}
|
|
|
|
|
2017-05-08 22:31:48 +02:00
|
|
|
@property (readonly, nonatomic) BOOL everHadGLContext;
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
- (void) addGLContext:(WineOpenGLContext*)context;
|
|
|
|
- (void) removeGLContext:(WineOpenGLContext*)context;
|
|
|
|
- (void) updateGLContexts;
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
- (void) wine_getBackingSize:(int*)outBackingSize;
|
|
|
|
- (void) wine_setBackingSize:(const int*)newBackingSize;
|
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
- (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
|
|
|
|
#endif
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface WineWindow ()
|
|
|
|
|
2013-06-18 06:35:56 +02:00
|
|
|
@property (readwrite, nonatomic) BOOL disabled;
|
|
|
|
@property (readwrite, nonatomic) BOOL noActivate;
|
2013-02-18 02:28:30 +01:00
|
|
|
@property (readwrite, nonatomic) BOOL floating;
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
@property (readwrite, nonatomic) BOOL drawnSinceShown;
|
2013-09-27 06:46:31 +02:00
|
|
|
@property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
|
2013-01-11 13:19:36 +01:00
|
|
|
@property (retain, nonatomic) NSWindow* latentParentWindow;
|
2013-01-11 13:18:05 +01:00
|
|
|
|
2013-01-21 07:08:03 +01:00
|
|
|
@property (nonatomic) void* hwnd;
|
2013-01-27 23:19:48 +01:00
|
|
|
@property (retain, readwrite, nonatomic) WineEventQueue* queue;
|
2013-01-21 07:07:58 +01:00
|
|
|
|
2013-01-15 03:23:55 +01:00
|
|
|
@property (nonatomic) void* surface;
|
|
|
|
@property (nonatomic) pthread_mutex_t* surface_mutex;
|
|
|
|
|
2013-01-15 03:23:58 +01:00
|
|
|
@property (copy, nonatomic) NSBezierPath* shape;
|
2014-10-03 00:06:19 +02:00
|
|
|
@property (copy, nonatomic) NSData* shapeData;
|
2013-01-15 03:23:58 +01:00
|
|
|
@property (nonatomic) BOOL shapeChangedSinceLastDraw;
|
|
|
|
@property (readonly, nonatomic) BOOL needsTransparency;
|
|
|
|
|
2013-01-15 03:24:02 +01:00
|
|
|
@property (nonatomic) BOOL colorKeyed;
|
|
|
|
@property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
|
|
|
|
@property (nonatomic) BOOL usePerPixelAlpha;
|
|
|
|
|
2013-04-22 04:32:26 +02:00
|
|
|
@property (assign, nonatomic) void* imeData;
|
|
|
|
@property (nonatomic) BOOL commandDone;
|
|
|
|
|
2014-12-17 15:59:09 +01:00
|
|
|
@property (readonly, copy, nonatomic) NSArray* childWineWindows;
|
|
|
|
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
- (void) updateForGLSubviews;
|
2013-07-02 08:24:19 +02:00
|
|
|
|
2013-09-03 05:48:59 +02:00
|
|
|
- (BOOL) becameEligibleParentOrChild;
|
|
|
|
- (void) becameIneligibleChild;
|
|
|
|
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
- (void) windowDidDrawContent;
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
2018-07-13 16:27:00 +02:00
|
|
|
@implementation WineBaseView
|
|
|
|
|
|
|
|
- (void) setRetinaMode:(int)mode
|
|
|
|
{
|
|
|
|
for (WineBaseView* subview in [self subviews])
|
|
|
|
{
|
|
|
|
if ([subview isKindOfClass:[WineBaseView class]])
|
|
|
|
[subview setRetinaMode:mode];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) preservesContentDuringLiveResize
|
|
|
|
{
|
|
|
|
// Returning YES from this tells Cocoa to keep our view's content during
|
|
|
|
// a Cocoa-driven resize. In theory, we're also supposed to override
|
|
|
|
// -setFrameSize: to mark exposed sections as needing redisplay, but
|
|
|
|
// user32 will take care of that in a roundabout way. This way, we don't
|
|
|
|
// redraw until the window surface is flushed.
|
|
|
|
//
|
|
|
|
// This doesn't do anything when we resize the window ourselves.
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return [[self window] contentView] == self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) mouseDownCanMoveWindow
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFocusRingType) focusRingType
|
|
|
|
{
|
|
|
|
return NSFocusRingTypeNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@implementation WineContentView
|
|
|
|
|
2017-05-08 22:31:48 +02:00
|
|
|
@synthesize everHadGLContext = _everHadGLContext;
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
2013-04-22 04:32:26 +02:00
|
|
|
[markedText release];
|
2013-03-06 11:59:07 +01:00
|
|
|
[glContexts release];
|
|
|
|
[pendingGlContexts release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
- (BOOL) isFlipped
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2013-01-15 03:23:55 +01:00
|
|
|
- (void) drawRect:(NSRect)rect
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)[self window];
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
for (WineOpenGLContext* context in pendingGlContexts)
|
2013-11-27 22:33:19 +01:00
|
|
|
{
|
|
|
|
if (!clearedGlSurface)
|
|
|
|
{
|
|
|
|
context.shouldClearToBlack = TRUE;
|
|
|
|
clearedGlSurface = TRUE;
|
|
|
|
}
|
2013-03-06 11:59:07 +01:00
|
|
|
context.needsUpdate = TRUE;
|
2013-11-27 22:33:19 +01:00
|
|
|
}
|
2013-03-06 11:59:07 +01:00
|
|
|
[glContexts addObjectsFromArray:pendingGlContexts];
|
|
|
|
[pendingGlContexts removeAllObjects];
|
|
|
|
|
|
|
|
if ([window contentView] != self)
|
|
|
|
return;
|
|
|
|
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
|
2014-10-03 00:06:14 +02:00
|
|
|
{
|
|
|
|
[[NSColor clearColor] setFill];
|
|
|
|
NSRectFill(rect);
|
|
|
|
|
|
|
|
[window.shape addClip];
|
|
|
|
|
|
|
|
[[NSColor windowBackgroundColor] setFill];
|
|
|
|
NSRectFill(rect);
|
|
|
|
}
|
|
|
|
|
2013-01-15 03:23:55 +01:00
|
|
|
if (window.surface && window.surface_mutex &&
|
|
|
|
!pthread_mutex_lock(window.surface_mutex))
|
|
|
|
{
|
|
|
|
const CGRect* rects;
|
|
|
|
int count;
|
|
|
|
|
2013-03-29 05:34:06 +01:00
|
|
|
if (get_surface_blit_rects(window.surface, &rects, &count) && count)
|
2013-03-29 05:34:03 +01:00
|
|
|
{
|
2016-05-05 20:53:36 +02:00
|
|
|
CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
|
2013-03-29 05:34:03 +01:00
|
|
|
CGContextRef context;
|
|
|
|
int i;
|
2013-01-15 03:23:55 +01:00
|
|
|
|
2013-03-29 05:34:03 +01:00
|
|
|
[window.shape addClip];
|
2013-01-15 03:23:55 +01:00
|
|
|
|
2013-03-29 05:34:03 +01:00
|
|
|
context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
|
|
CGContextSetBlendMode(context, kCGBlendModeCopy);
|
2016-05-05 20:53:36 +02:00
|
|
|
CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
|
2013-01-15 03:23:55 +01:00
|
|
|
|
2013-03-29 05:34:03 +01:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
CGRect imageRect;
|
|
|
|
CGImageRef image;
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
imageRect = CGRectIntersection(rects[i], dirtyRect);
|
2013-03-29 05:34:03 +01:00
|
|
|
image = create_surface_image(window.surface, &imageRect, FALSE);
|
2013-01-15 03:23:58 +01:00
|
|
|
|
2013-03-29 05:34:03 +01:00
|
|
|
if (image)
|
2013-01-15 03:24:02 +01:00
|
|
|
{
|
2013-03-29 05:34:03 +01:00
|
|
|
if (window.colorKeyed)
|
2013-01-15 03:24:02 +01:00
|
|
|
{
|
2013-03-29 05:34:03 +01:00
|
|
|
CGImageRef maskedImage;
|
|
|
|
CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
|
|
|
|
window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
|
|
|
|
window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
|
|
|
|
maskedImage = CGImageCreateWithMaskingColors(image, components);
|
|
|
|
if (maskedImage)
|
|
|
|
{
|
|
|
|
CGImageRelease(image);
|
|
|
|
image = maskedImage;
|
|
|
|
}
|
2013-01-15 03:24:02 +01:00
|
|
|
}
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
|
2013-01-15 03:23:55 +01:00
|
|
|
|
2013-03-29 05:34:03 +01:00
|
|
|
CGImageRelease(image);
|
|
|
|
}
|
2013-01-15 03:23:55 +01:00
|
|
|
}
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
|
|
|
|
[window windowDidDrawContent];
|
2013-01-15 03:23:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(window.surface_mutex);
|
|
|
|
}
|
2013-03-29 05:33:59 +01:00
|
|
|
|
|
|
|
// If the window may be transparent, then we have to invalidate the
|
|
|
|
// shadow every time we draw. Also, if this is the first time we've
|
|
|
|
// drawn since changing from transparent to opaque.
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
|
2013-03-29 05:33:59 +01:00
|
|
|
{
|
|
|
|
window.shapeChangedSinceLastDraw = FALSE;
|
|
|
|
[window invalidateShadow];
|
|
|
|
}
|
2013-01-15 03:23:55 +01:00
|
|
|
}
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
- (void) addGLContext:(WineOpenGLContext*)context
|
|
|
|
{
|
2017-05-08 22:31:48 +02:00
|
|
|
BOOL hadContext = _everHadGLContext;
|
2013-03-06 11:59:07 +01:00
|
|
|
if (!glContexts)
|
|
|
|
glContexts = [[NSMutableArray alloc] init];
|
|
|
|
if (!pendingGlContexts)
|
|
|
|
pendingGlContexts = [[NSMutableArray alloc] init];
|
2013-11-27 22:32:46 +01:00
|
|
|
|
|
|
|
if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
|
|
|
|
{
|
|
|
|
[glContexts addObject:context];
|
2013-11-27 22:33:19 +01:00
|
|
|
if (!clearedGlSurface)
|
|
|
|
{
|
|
|
|
context.shouldClearToBlack = TRUE;
|
|
|
|
clearedGlSurface = TRUE;
|
|
|
|
}
|
2013-11-27 22:32:46 +01:00
|
|
|
context.needsUpdate = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[pendingGlContexts addObject:context];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
|
2017-05-08 22:31:48 +02:00
|
|
|
_everHadGLContext = YES;
|
2016-05-13 01:50:39 +02:00
|
|
|
if (!hadContext)
|
|
|
|
[self invalidateHasGLDescendant];
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
[(WineWindow*)[self window] updateForGLSubviews];
|
2013-03-06 11:59:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeGLContext:(WineOpenGLContext*)context
|
|
|
|
{
|
|
|
|
[glContexts removeObjectIdenticalTo:context];
|
|
|
|
[pendingGlContexts removeObjectIdenticalTo:context];
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
[(WineWindow*)[self window] updateForGLSubviews];
|
2013-03-06 11:59:07 +01:00
|
|
|
}
|
|
|
|
|
2017-02-02 22:16:27 +01:00
|
|
|
- (void) updateGLContexts:(BOOL)reattach
|
2013-03-06 11:59:07 +01:00
|
|
|
{
|
|
|
|
for (WineOpenGLContext* context in glContexts)
|
2017-02-02 22:16:27 +01:00
|
|
|
{
|
2013-03-06 11:59:07 +01:00
|
|
|
context.needsUpdate = TRUE;
|
2017-02-02 22:16:27 +01:00
|
|
|
if (reattach)
|
|
|
|
context.needsReattach = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) updateGLContexts
|
|
|
|
{
|
|
|
|
[self updateGLContexts:NO];
|
2013-03-06 11:59:07 +01:00
|
|
|
}
|
|
|
|
|
2016-05-13 01:50:39 +02:00
|
|
|
- (BOOL) _hasGLDescendant
|
|
|
|
{
|
2016-05-13 01:50:42 +02:00
|
|
|
if ([self isHidden])
|
|
|
|
return NO;
|
2017-05-08 22:31:48 +02:00
|
|
|
if (_everHadGLContext)
|
2016-05-13 01:50:39 +02:00
|
|
|
return YES;
|
|
|
|
for (WineContentView* view in [self subviews])
|
|
|
|
{
|
2018-07-13 16:27:00 +02:00
|
|
|
if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
|
2016-05-13 01:50:39 +02:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasGLDescendant
|
|
|
|
{
|
|
|
|
if (!_cachedHasGLDescendantValid)
|
|
|
|
{
|
|
|
|
_cachedHasGLDescendant = [self _hasGLDescendant];
|
|
|
|
_cachedHasGLDescendantValid = YES;
|
|
|
|
}
|
|
|
|
return _cachedHasGLDescendant;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) invalidateHasGLDescendant
|
|
|
|
{
|
|
|
|
BOOL invalidateAncestors = _cachedHasGLDescendantValid;
|
|
|
|
_cachedHasGLDescendantValid = NO;
|
|
|
|
if (invalidateAncestors && self != [[self window] contentView])
|
|
|
|
{
|
|
|
|
WineContentView* superview = (WineContentView*)[self superview];
|
|
|
|
if ([superview isKindOfClass:[WineContentView class]])
|
|
|
|
[superview invalidateHasGLDescendant];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
- (void) wine_getBackingSize:(int*)outBackingSize
|
|
|
|
{
|
|
|
|
@synchronized(self) {
|
|
|
|
memcpy(outBackingSize, backingSize, sizeof(backingSize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
- (void) wine_setBackingSize:(const int*)newBackingSize
|
|
|
|
{
|
|
|
|
@synchronized(self) {
|
|
|
|
memcpy(backingSize, newBackingSize, sizeof(backingSize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
- (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
|
|
|
|
{
|
|
|
|
if (_metalView) return _metalView;
|
|
|
|
|
|
|
|
WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
|
|
|
|
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
|
|
|
[self setAutoresizesSubviews:YES];
|
|
|
|
[self addSubview:view positioned:NSWindowBelow relativeTo:nil];
|
|
|
|
_metalView = view;
|
|
|
|
|
|
|
|
[(WineWindow*)self.window windowDidDrawContent];
|
|
|
|
|
|
|
|
return _metalView;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-19 01:38:25 +02:00
|
|
|
- (void) setRetinaMode:(int)mode
|
|
|
|
{
|
|
|
|
double scale = mode ? 0.5 : 2.0;
|
|
|
|
NSRect frame = self.frame;
|
|
|
|
frame.origin.x *= scale;
|
|
|
|
frame.origin.y *= scale;
|
|
|
|
frame.size.width *= scale;
|
|
|
|
frame.size.height *= scale;
|
|
|
|
[self setFrame:frame];
|
|
|
|
[self updateGLContexts];
|
|
|
|
|
2018-07-13 16:27:00 +02:00
|
|
|
[super setRetinaMode:mode];
|
2014-10-28 04:54:29 +01:00
|
|
|
}
|
|
|
|
|
2017-02-02 22:16:27 +01:00
|
|
|
- (void) viewDidHide
|
|
|
|
{
|
|
|
|
[super viewDidHide];
|
2017-04-21 03:53:16 +02:00
|
|
|
[self invalidateHasGLDescendant];
|
2017-02-02 22:16:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) viewDidUnhide
|
|
|
|
{
|
|
|
|
[super viewDidUnhide];
|
2017-04-21 03:53:16 +02:00
|
|
|
[self updateGLContexts:YES];
|
|
|
|
[self invalidateHasGLDescendant];
|
2017-02-02 22:16:27 +01:00
|
|
|
}
|
|
|
|
|
2019-05-06 18:30:14 +02:00
|
|
|
- (void) clearMarkedText
|
|
|
|
{
|
|
|
|
[markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
|
|
|
|
markedTextSelection = NSMakeRange(0, 0);
|
|
|
|
[[self inputContext] discardMarkedText];
|
|
|
|
}
|
|
|
|
|
2013-04-22 04:32:26 +02:00
|
|
|
- (void) completeText:(NSString*)text
|
|
|
|
{
|
|
|
|
macdrv_event* event;
|
|
|
|
WineWindow* window = (WineWindow*)[self window];
|
|
|
|
|
|
|
|
event = macdrv_create_event(IM_SET_TEXT, window);
|
|
|
|
event->im_set_text.data = [window imeData];
|
|
|
|
event->im_set_text.text = (CFStringRef)[text copy];
|
|
|
|
event->im_set_text.complete = TRUE;
|
|
|
|
|
|
|
|
[[window queue] postEvent:event];
|
|
|
|
|
|
|
|
macdrv_release_event(event);
|
|
|
|
|
2019-05-06 18:30:14 +02:00
|
|
|
[self clearMarkedText];
|
2013-04-22 04:32:26 +02:00
|
|
|
}
|
|
|
|
|
2016-05-13 01:50:39 +02:00
|
|
|
- (void) didAddSubview:(NSView*)subview
|
|
|
|
{
|
|
|
|
if ([subview isKindOfClass:[WineContentView class]])
|
|
|
|
{
|
|
|
|
WineContentView* view = (WineContentView*)subview;
|
|
|
|
if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
|
|
|
|
[self invalidateHasGLDescendant];
|
|
|
|
}
|
|
|
|
[super didAddSubview:subview];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) willRemoveSubview:(NSView*)subview
|
|
|
|
{
|
|
|
|
if ([subview isKindOfClass:[WineContentView class]])
|
|
|
|
{
|
|
|
|
WineContentView* view = (WineContentView*)subview;
|
|
|
|
if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
|
|
|
|
[self invalidateHasGLDescendant];
|
|
|
|
}
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
if (subview == _metalView)
|
|
|
|
_metalView = nil;
|
|
|
|
#endif
|
2016-05-13 01:50:39 +02:00
|
|
|
[super willRemoveSubview:subview];
|
|
|
|
}
|
|
|
|
|
2019-02-07 20:57:22 +01:00
|
|
|
- (void) setLayer:(CALayer*)newLayer
|
|
|
|
{
|
|
|
|
[super setLayer:newLayer];
|
|
|
|
[self updateGLContexts];
|
|
|
|
}
|
|
|
|
|
2013-04-22 04:32:26 +02:00
|
|
|
/*
|
|
|
|
* ---------- NSTextInputClient methods ----------
|
|
|
|
*/
|
|
|
|
- (NSTextInputContext*) inputContext
|
|
|
|
{
|
|
|
|
if (!markedText)
|
|
|
|
markedText = [[NSMutableAttributedString alloc] init];
|
|
|
|
return [super inputContext];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) insertText:(id)string replacementRange:(NSRange)replacementRange
|
|
|
|
{
|
|
|
|
if ([string isKindOfClass:[NSAttributedString class]])
|
|
|
|
string = [string string];
|
|
|
|
|
|
|
|
if ([string isKindOfClass:[NSString class]])
|
|
|
|
[self completeText:string];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) doCommandBySelector:(SEL)aSelector
|
|
|
|
{
|
|
|
|
[(WineWindow*)[self window] setCommandDone:TRUE];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
|
|
|
|
{
|
|
|
|
if ([string isKindOfClass:[NSAttributedString class]])
|
|
|
|
string = [string string];
|
|
|
|
|
|
|
|
if ([string isKindOfClass:[NSString class]])
|
|
|
|
{
|
|
|
|
macdrv_event* event;
|
|
|
|
WineWindow* window = (WineWindow*)[self window];
|
|
|
|
|
|
|
|
if (replacementRange.location == NSNotFound)
|
|
|
|
replacementRange = NSMakeRange(0, [markedText length]);
|
|
|
|
|
|
|
|
[markedText replaceCharactersInRange:replacementRange withString:string];
|
|
|
|
markedTextSelection = selectedRange;
|
|
|
|
markedTextSelection.location += replacementRange.location;
|
|
|
|
|
|
|
|
event = macdrv_create_event(IM_SET_TEXT, window);
|
|
|
|
event->im_set_text.data = [window imeData];
|
|
|
|
event->im_set_text.text = (CFStringRef)[[markedText string] copy];
|
|
|
|
event->im_set_text.complete = FALSE;
|
2014-05-23 15:04:36 +02:00
|
|
|
event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
|
2013-04-22 04:32:26 +02:00
|
|
|
|
|
|
|
[[window queue] postEvent:event];
|
|
|
|
|
|
|
|
macdrv_release_event(event);
|
2013-05-16 23:48:39 +02:00
|
|
|
|
|
|
|
[[self inputContext] invalidateCharacterCoordinates];
|
2013-04-22 04:32:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) unmarkText
|
|
|
|
{
|
|
|
|
[self completeText:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRange) selectedRange
|
|
|
|
{
|
|
|
|
return markedTextSelection;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRange) markedRange
|
|
|
|
{
|
|
|
|
NSRange range = NSMakeRange(0, [markedText length]);
|
|
|
|
if (!range.length)
|
|
|
|
range.location = NSNotFound;
|
|
|
|
return range;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasMarkedText
|
|
|
|
{
|
|
|
|
return [markedText length] > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
|
|
|
{
|
|
|
|
if (aRange.location >= [markedText length])
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
|
|
|
|
if (actualRange)
|
|
|
|
*actualRange = aRange;
|
|
|
|
return [markedText attributedSubstringFromRange:aRange];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*) validAttributesForMarkedText
|
|
|
|
{
|
|
|
|
return [NSArray array];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
|
|
|
{
|
2013-05-16 23:48:39 +02:00
|
|
|
macdrv_query* query;
|
|
|
|
WineWindow* window = (WineWindow*)[self window];
|
|
|
|
NSRect ret;
|
|
|
|
|
2013-04-22 04:32:26 +02:00
|
|
|
aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
|
2013-05-16 23:48:39 +02:00
|
|
|
|
|
|
|
query = macdrv_create_query();
|
|
|
|
query->type = QUERY_IME_CHAR_RECT;
|
|
|
|
query->window = (macdrv_window)[window retain];
|
|
|
|
query->ime_char_rect.data = [window imeData];
|
|
|
|
query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
|
|
|
|
|
2016-02-05 00:18:27 +01:00
|
|
|
if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
|
2013-05-16 23:48:39 +02:00
|
|
|
{
|
|
|
|
aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
|
2016-05-05 20:53:36 +02:00
|
|
|
ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
|
2013-05-16 23:48:39 +02:00
|
|
|
[[WineApplicationController sharedController] flipRect:&ret];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
|
|
|
|
|
|
|
|
macdrv_release_query(query);
|
|
|
|
|
2013-04-22 04:32:26 +02:00
|
|
|
if (actualRange)
|
|
|
|
*actualRange = aRange;
|
2013-05-16 23:48:39 +02:00
|
|
|
return ret;
|
2013-04-22 04:32:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
|
|
|
|
{
|
|
|
|
return NSNotFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSInteger) windowLevel
|
|
|
|
{
|
|
|
|
return [[self window] level];
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
@implementation WineMetalView
|
|
|
|
|
|
|
|
- (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
|
|
|
|
{
|
|
|
|
self = [super initWithFrame:frame];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
_device = [device retain];
|
|
|
|
self.wantsLayer = YES;
|
|
|
|
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[_device release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setRetinaMode:(int)mode
|
|
|
|
{
|
|
|
|
self.layer.contentsScale = mode ? 2.0 : 1.0;
|
|
|
|
[super setRetinaMode:mode];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CALayer*) makeBackingLayer
|
|
|
|
{
|
|
|
|
CAMetalLayer *layer = [CAMetalLayer layer];
|
|
|
|
layer.device = _device;
|
|
|
|
layer.framebufferOnly = YES;
|
|
|
|
layer.magnificationFilter = kCAFilterNearest;
|
|
|
|
layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
|
|
|
|
layer.contentsScale = retina_on ? 2.0 : 1.0;
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) isOpaque
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@implementation WineWindow
|
|
|
|
|
2013-09-25 22:10:57 +02:00
|
|
|
static WineWindow* causing_becomeKeyWindow;
|
|
|
|
|
2013-09-27 06:46:31 +02:00
|
|
|
@synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
@synthesize drawnSinceShown;
|
2013-01-15 03:23:55 +01:00
|
|
|
@synthesize surface, surface_mutex;
|
2014-10-03 00:06:19 +02:00
|
|
|
@synthesize shape, shapeData, shapeChangedSinceLastDraw;
|
2013-01-15 03:24:02 +01:00
|
|
|
@synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
|
|
|
|
@synthesize usePerPixelAlpha;
|
2013-04-22 04:32:26 +02:00
|
|
|
@synthesize imeData, commandDone;
|
2013-01-11 13:18:05 +01:00
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
+ (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
|
|
|
|
windowFrame:(NSRect)window_frame
|
2013-01-21 07:08:03 +01:00
|
|
|
hwnd:(void*)hwnd
|
2013-01-21 07:07:58 +01:00
|
|
|
queue:(WineEventQueue*)queue
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
|
|
|
WineWindow* window;
|
|
|
|
WineContentView* contentView;
|
2013-02-07 02:32:26 +01:00
|
|
|
NSTrackingArea* trackingArea;
|
2013-12-31 08:05:09 +01:00
|
|
|
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-04-16 07:38:01 +02:00
|
|
|
[[WineApplicationController sharedController] flipRect:&window_frame];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
|
|
|
window = [[[self alloc] initWithContentRect:window_frame
|
|
|
|
styleMask:style_mask_for_features(wf)
|
|
|
|
backing:NSBackingStoreBuffered
|
|
|
|
defer:YES] autorelease];
|
|
|
|
|
2013-01-11 13:18:05 +01:00
|
|
|
if (!window) return nil;
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
/* Standardize windows to eliminate differences between titled and
|
|
|
|
borderless windows and between NSWindow and NSPanel. */
|
|
|
|
[window setHidesOnDeactivate:NO];
|
|
|
|
[window setReleasedWhenClosed:NO];
|
|
|
|
|
2013-09-27 06:46:31 +02:00
|
|
|
[window setOneShot:YES];
|
2013-01-07 21:44:44 +01:00
|
|
|
[window disableCursorRects];
|
|
|
|
[window setShowsResizeIndicator:NO];
|
|
|
|
[window setHasShadow:wf->shadow];
|
2013-02-25 05:53:37 +01:00
|
|
|
[window setAcceptsMouseMovedEvents:YES];
|
2013-01-07 21:44:44 +01:00
|
|
|
[window setDelegate:window];
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
[window setBackgroundColor:[NSColor clearColor]];
|
|
|
|
[window setOpaque:NO];
|
2013-01-21 07:08:03 +01:00
|
|
|
window.hwnd = hwnd;
|
2013-01-21 07:07:58 +01:00
|
|
|
window.queue = queue;
|
2013-09-18 20:00:37 +02:00
|
|
|
window->savedContentMinSize = NSZeroSize;
|
|
|
|
window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
|
2013-12-30 04:34:48 +01:00
|
|
|
window->resizable = wf->resizable;
|
2015-11-10 03:31:22 +01:00
|
|
|
window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-03-13 22:53:32 +01:00
|
|
|
[window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
|
|
|
|
(NSString*)kUTTypeContent,
|
|
|
|
nil]];
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
|
|
|
|
if (!contentView)
|
|
|
|
return nil;
|
|
|
|
[contentView setAutoresizesSubviews:NO];
|
|
|
|
|
2013-02-25 05:53:37 +01:00
|
|
|
/* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
|
|
|
|
because they give us mouse moves in the background. */
|
2013-02-07 02:32:26 +01:00
|
|
|
trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
|
2013-02-25 05:53:32 +01:00
|
|
|
options:(NSTrackingMouseMoved |
|
2013-02-07 02:32:26 +01:00
|
|
|
NSTrackingActiveAlways |
|
|
|
|
NSTrackingInVisibleRect)
|
|
|
|
owner:window
|
|
|
|
userInfo:nil] autorelease];
|
|
|
|
if (!trackingArea)
|
|
|
|
return nil;
|
|
|
|
[contentView addTrackingArea:trackingArea];
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
[window setContentView:contentView];
|
2013-04-22 04:32:26 +02:00
|
|
|
[window setInitialFirstResponder:contentView];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-12-31 08:05:09 +01:00
|
|
|
[nc addObserver:window
|
|
|
|
selector:@selector(updateFullscreen)
|
|
|
|
name:NSApplicationDidChangeScreenParametersNotification
|
|
|
|
object:NSApp];
|
2013-06-07 00:43:06 +02:00
|
|
|
[window updateFullscreen];
|
|
|
|
|
2013-12-31 08:05:09 +01:00
|
|
|
[nc addObserver:window
|
|
|
|
selector:@selector(applicationWillHide)
|
|
|
|
name:NSApplicationWillHideNotification
|
|
|
|
object:NSApp];
|
|
|
|
[nc addObserver:window
|
|
|
|
selector:@selector(applicationDidUnhide)
|
|
|
|
name:NSApplicationDidUnhideNotification
|
|
|
|
object:NSApp];
|
|
|
|
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
|
|
|
|
selector:@selector(checkWineDisplayLink)
|
|
|
|
name:NSWorkspaceActiveSpaceDidChangeNotification
|
|
|
|
object:[NSWorkspace sharedWorkspace]];
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
[window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
return window;
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:19:36 +01:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
2015-11-30 21:34:59 +01:00
|
|
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
2013-06-07 00:43:06 +02:00
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
2013-01-21 07:07:58 +01:00
|
|
|
[queue release];
|
2013-08-30 07:00:56 +02:00
|
|
|
[latentChildWindows release];
|
2013-01-11 13:19:36 +01:00
|
|
|
[latentParentWindow release];
|
2013-01-15 03:23:58 +01:00
|
|
|
[shape release];
|
2014-10-03 00:06:19 +02:00
|
|
|
[shapeData release];
|
2013-01-11 13:19:36 +01:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
- (BOOL) preventResizing
|
|
|
|
{
|
2014-04-28 22:09:28 +02:00
|
|
|
BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
|
2015-03-24 00:58:06 +01:00
|
|
|
return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
|
2013-12-30 04:34:48 +01:00
|
|
|
}
|
|
|
|
|
2015-03-24 00:58:11 +01:00
|
|
|
- (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
|
|
|
|
{
|
|
|
|
if (allow_immovable_windows && (disabled || inMaximized))
|
|
|
|
return NO;
|
|
|
|
else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
|
|
|
|
return NO;
|
|
|
|
else
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:18:05 +01:00
|
|
|
- (void) adjustFeaturesForState
|
|
|
|
{
|
2013-05-14 10:35:03 +02:00
|
|
|
NSUInteger style = [self styleMask];
|
2013-01-11 13:18:05 +01:00
|
|
|
|
|
|
|
if (style & NSClosableWindowMask)
|
|
|
|
[[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
|
|
|
|
if (style & NSMiniaturizableWindowMask)
|
|
|
|
[[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
|
2013-05-14 10:35:03 +02:00
|
|
|
if (style & NSResizableWindowMask)
|
|
|
|
[[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
|
2013-10-10 21:22:08 +02:00
|
|
|
if ([self respondsToSelector:@selector(toggleFullScreen:)])
|
|
|
|
{
|
|
|
|
if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
|
|
|
|
[[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
|
|
|
|
}
|
2013-12-30 04:33:20 +01:00
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
if ([self preventResizing])
|
2013-12-30 04:33:20 +01:00
|
|
|
{
|
2016-05-05 20:53:36 +02:00
|
|
|
NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
|
2013-12-30 04:33:20 +01:00
|
|
|
[self setContentMinSize:size];
|
|
|
|
[self setContentMaxSize:size];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self setContentMaxSize:savedContentMaxSize];
|
|
|
|
[self setContentMinSize:savedContentMinSize];
|
|
|
|
}
|
2013-12-30 04:33:30 +01:00
|
|
|
|
2014-04-28 22:09:28 +02:00
|
|
|
if (allow_immovable_windows || cursor_clipping_locks_windows)
|
2015-03-24 00:58:11 +01:00
|
|
|
[self setMovable:[self allowsMovingWithMaximized:maximized]];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
|
|
|
|
{
|
|
|
|
if ([self respondsToSelector:@selector(toggleFullScreen:)])
|
|
|
|
{
|
|
|
|
NSUInteger style = [self styleMask];
|
|
|
|
|
|
|
|
if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
|
2017-04-24 20:07:15 +02:00
|
|
|
style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
|
|
|
|
!(self.parentWindow || self.latentParentWindow))
|
2013-10-10 21:22:08 +02:00
|
|
|
{
|
|
|
|
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
|
|
|
|
behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
|
|
|
|
behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
|
|
|
|
if (style & NSFullScreenWindowMask)
|
2015-03-13 00:44:44 +01:00
|
|
|
[super toggleFullScreen:nil];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (behavior != [self collectionBehavior])
|
|
|
|
{
|
|
|
|
[self setCollectionBehavior:behavior];
|
|
|
|
[self adjustFeaturesForState];
|
|
|
|
}
|
2013-01-11 13:18:05 +01:00
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
- (void) setWindowFeatures:(const struct macdrv_window_features*)wf
|
|
|
|
{
|
2013-10-08 09:21:27 +02:00
|
|
|
static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
|
|
|
|
NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
|
2013-05-14 10:35:03 +02:00
|
|
|
NSUInteger currentStyle = [self styleMask];
|
2013-10-08 09:21:27 +02:00
|
|
|
NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
|
2013-05-14 10:35:03 +02:00
|
|
|
|
|
|
|
if (newStyle != currentStyle)
|
|
|
|
{
|
2013-10-17 10:54:00 +02:00
|
|
|
NSString* title = [[[self title] copy] autorelease];
|
2013-05-14 10:35:03 +02:00
|
|
|
BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
|
|
|
|
BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
|
|
|
|
if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
|
|
|
|
{
|
|
|
|
// -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
|
|
|
|
// If transitioning from NSTitledWindowMask | NSResizableWindowMask to
|
|
|
|
// just NSTitledWindowMask, the window buttons should disappear rather
|
|
|
|
// than just being disabled. But they don't. Similarly in reverse.
|
|
|
|
// The workaround is to also toggle NSClosableWindowMask at the same time.
|
|
|
|
[self setStyleMask:newStyle ^ NSClosableWindowMask];
|
|
|
|
}
|
|
|
|
[self setStyleMask:newStyle];
|
2014-03-11 00:23:17 +01:00
|
|
|
|
|
|
|
// -setStyleMask: resets the firstResponder to the window. Set it
|
|
|
|
// back to the content view.
|
|
|
|
if ([[self contentView] acceptsFirstResponder])
|
|
|
|
[self makeFirstResponder:[self contentView]];
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
[self adjustFullScreenBehavior:[self collectionBehavior]];
|
2013-10-17 10:54:00 +02:00
|
|
|
|
|
|
|
if ([[self title] length] == 0 && [title length] > 0)
|
|
|
|
[self setTitle:title];
|
2013-05-14 10:35:03 +02:00
|
|
|
}
|
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
resizable = wf->resizable;
|
2013-01-11 13:18:05 +01:00
|
|
|
[self adjustFeaturesForState];
|
2013-01-07 21:44:44 +01:00
|
|
|
[self setHasShadow:wf->shadow];
|
|
|
|
}
|
|
|
|
|
2013-12-31 08:05:09 +01:00
|
|
|
// Indicates if the window would be visible if the app were not hidden.
|
|
|
|
- (BOOL) wouldBeVisible
|
|
|
|
{
|
|
|
|
return [NSApp isHidden] ? savedVisibleState : [self isVisible];
|
|
|
|
}
|
|
|
|
|
2013-05-17 01:43:25 +02:00
|
|
|
- (BOOL) isOrderedIn
|
|
|
|
{
|
2013-12-31 08:05:09 +01:00
|
|
|
return [self wouldBeVisible] || [self isMiniaturized];
|
2013-05-17 01:43:25 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
- (NSInteger) minimumLevelForActive:(BOOL)active
|
2013-01-11 13:18:05 +01:00
|
|
|
{
|
2013-01-11 13:18:55 +01:00
|
|
|
NSInteger level;
|
2013-02-18 02:28:30 +01:00
|
|
|
|
2013-05-29 16:21:25 +02:00
|
|
|
if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
|
|
|
|
(topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
|
2013-02-18 02:28:30 +01:00
|
|
|
level = NSFloatingWindowLevel;
|
|
|
|
else
|
|
|
|
level = NSNormalWindowLevel;
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
if (active)
|
2013-02-18 02:28:30 +01:00
|
|
|
{
|
2013-05-29 16:21:25 +02:00
|
|
|
BOOL captured;
|
2013-02-18 02:28:30 +01:00
|
|
|
|
2013-06-07 00:43:06 +02:00
|
|
|
captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
|
2013-05-17 01:43:33 +02:00
|
|
|
|
|
|
|
if (captured || fullscreen)
|
2013-02-18 02:28:30 +01:00
|
|
|
{
|
2013-05-17 01:43:33 +02:00
|
|
|
if (captured)
|
|
|
|
level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
|
|
|
|
else
|
2015-02-04 18:42:03 +01:00
|
|
|
level = NSStatusWindowLevel + 1;
|
2013-05-17 01:43:33 +02:00
|
|
|
|
|
|
|
if (self.floating)
|
|
|
|
level++;
|
2013-02-18 02:28:30 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-17 01:43:33 +02:00
|
|
|
|
|
|
|
return level;
|
2013-02-18 02:28:30 +01:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
- (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);
|
|
|
|
}
|
|
|
|
|
2015-03-24 00:58:06 +01:00
|
|
|
- (void) sendResizeStartQuery
|
|
|
|
{
|
|
|
|
macdrv_query* query = macdrv_create_query();
|
|
|
|
query->type = QUERY_RESIZE_START;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
|
|
|
|
|
|
|
[self.queue query:query timeout:0.3];
|
|
|
|
macdrv_release_query(query);
|
|
|
|
}
|
|
|
|
|
2013-02-18 02:28:30 +01:00
|
|
|
- (void) setMacDrvState:(const struct macdrv_window_state*)state
|
|
|
|
{
|
2013-01-11 13:20:40 +01:00
|
|
|
NSWindowCollectionBehavior behavior;
|
2013-01-11 13:18:55 +01:00
|
|
|
|
2013-01-11 13:18:05 +01:00
|
|
|
self.disabled = state->disabled;
|
2013-01-11 13:18:36 +01:00
|
|
|
self.noActivate = state->no_activate;
|
2013-01-11 13:18:55 +01:00
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
if (self.floating != state->floating)
|
|
|
|
{
|
|
|
|
self.floating = state->floating;
|
2013-09-03 05:48:59 +02:00
|
|
|
if (state->floating)
|
|
|
|
{
|
|
|
|
// Became floating. If child of non-floating window, make that
|
|
|
|
// relationship latent.
|
|
|
|
WineWindow* parent = (WineWindow*)[self parentWindow];
|
|
|
|
if (parent && !parent.floating)
|
|
|
|
[self becameIneligibleChild];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Became non-floating. If parent of floating children, make that
|
|
|
|
// relationship latent.
|
|
|
|
WineWindow* child;
|
2014-12-17 15:59:09 +01:00
|
|
|
for (child in [self childWineWindows])
|
2013-09-03 05:48:59 +02:00
|
|
|
{
|
|
|
|
if (child.floating)
|
|
|
|
[child becameIneligibleChild];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check our latent relationships. If floating status was the only
|
|
|
|
// reason they were latent, then make them active.
|
|
|
|
if ([self isVisible])
|
|
|
|
[self becameEligibleParentOrChild];
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
[[WineApplicationController sharedController] adjustWindowLevels];
|
|
|
|
}
|
2013-01-11 13:20:40 +01:00
|
|
|
|
2013-10-08 09:21:24 +02:00
|
|
|
if (state->minimized_valid)
|
2013-02-04 00:20:07 +01:00
|
|
|
{
|
2013-12-31 08:05:18 +01:00
|
|
|
macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
|
2013-10-10 21:22:08 +02:00
|
|
|
|
2013-10-08 09:21:24 +02:00
|
|
|
pendingMinimize = FALSE;
|
|
|
|
if (state->minimized && ![self isMiniaturized])
|
|
|
|
{
|
2013-12-31 08:05:09 +01:00
|
|
|
if ([self wouldBeVisible])
|
2013-10-10 21:22:08 +02:00
|
|
|
{
|
|
|
|
if ([self styleMask] & NSFullScreenWindowMask)
|
|
|
|
{
|
|
|
|
[self postDidUnminimizeEvent];
|
2013-12-31 08:05:18 +01:00
|
|
|
discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
else
|
2013-12-31 08:05:18 +01:00
|
|
|
{
|
2013-10-10 21:22:08 +02:00
|
|
|
[super miniaturize:nil];
|
2013-12-31 08:05:18 +01:00
|
|
|
discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
|
|
|
|
event_mask_for_type(WINDOW_GOT_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_LOST_FOCUS);
|
|
|
|
}
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
2013-10-08 09:21:24 +02:00
|
|
|
else
|
|
|
|
pendingMinimize = TRUE;
|
|
|
|
}
|
|
|
|
else if (!state->minimized && [self isMiniaturized])
|
|
|
|
{
|
|
|
|
ignore_windowDeminiaturize = TRUE;
|
|
|
|
[self deminiaturize:nil];
|
2013-12-31 08:05:18 +01:00
|
|
|
discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
|
2013-10-08 09:21:24 +02:00
|
|
|
}
|
2013-02-04 00:20:07 +01:00
|
|
|
|
2013-12-31 08:05:18 +01:00
|
|
|
if (discard)
|
|
|
|
[queue discardEventsMatchingMask:discard forWindow:self];
|
2013-10-08 09:21:24 +02:00
|
|
|
}
|
2013-12-30 04:34:48 +01:00
|
|
|
|
|
|
|
if (state->maximized != maximized)
|
|
|
|
{
|
|
|
|
maximized = state->maximized;
|
|
|
|
[self adjustFeaturesForState];
|
2015-03-24 00:58:06 +01:00
|
|
|
|
|
|
|
if (!maximized && [self inLiveResize])
|
|
|
|
[self sendResizeStartQuery];
|
2013-12-30 04:34:48 +01:00
|
|
|
}
|
winemac: Prevent maximized windows from entering Cocoa full-screen mode.
OS X doesn't really have the concept of windows being maximized; that is, being
in a mode where they can't be moved or resized. As a consequence, it doesn't
have a button in the window title bar to restore a maximized window to normal.
So, when a Wine window is maximized, the Mac driver hijacks the green zoom
button to act as a restore button. (When a window is zoomed, the green button
"unzooms" back to its last user size and position, so it's analogous.)
However, with OS X 10.10 (Yosemite), the green button prefers to act as a
toggle for the Cocoa full-screen mode rather than zooming and unzooming. This
made it difficult for users to restore a maximized window. They would have to
Option-click the green button, double-click the title bar, or choose Zoom
from the Window menu, none of which is obvious.
The fix is to disable Cocoa full-screen mode for maximized windows. Then, the
green button reverts to unzoom and restoring the window.
2015-03-13 00:45:25 +01:00
|
|
|
|
|
|
|
behavior = NSWindowCollectionBehaviorDefault;
|
|
|
|
if (state->excluded_by_expose)
|
|
|
|
behavior |= NSWindowCollectionBehaviorTransient;
|
|
|
|
else
|
|
|
|
behavior |= NSWindowCollectionBehaviorManaged;
|
|
|
|
if (state->excluded_by_cycle)
|
|
|
|
{
|
|
|
|
behavior |= NSWindowCollectionBehaviorIgnoresCycle;
|
|
|
|
if ([self isOrderedIn])
|
|
|
|
[NSApp removeWindowsItem:self];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
|
|
|
|
if ([self isOrderedIn])
|
|
|
|
[NSApp addWindowsItem:self title:[self title] filename:NO];
|
|
|
|
}
|
|
|
|
[self adjustFullScreenBehavior:behavior];
|
2013-01-11 13:18:05 +01:00
|
|
|
}
|
|
|
|
|
2013-08-30 07:00:49 +02:00
|
|
|
- (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
|
|
|
|
{
|
|
|
|
BOOL reordered = FALSE;
|
|
|
|
|
2013-09-03 05:48:59 +02:00
|
|
|
if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
|
2013-08-30 07:00:49 +02:00
|
|
|
{
|
|
|
|
if ([self level] > [child level])
|
|
|
|
[child setLevel:[self level]];
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
if (![child isVisible])
|
|
|
|
[child setAutodisplay:YES];
|
2013-08-30 07:00:49 +02:00
|
|
|
[self addChildWindow:child ordered:NSWindowAbove];
|
2015-11-10 03:31:21 +01:00
|
|
|
[child checkWineDisplayLink];
|
2013-08-30 07:00:56 +02:00
|
|
|
[latentChildWindows removeObjectIdenticalTo:child];
|
2013-08-30 07:00:49 +02:00
|
|
|
child.latentParentWindow = nil;
|
|
|
|
reordered = TRUE;
|
|
|
|
}
|
|
|
|
else
|
2013-08-30 07:00:56 +02:00
|
|
|
{
|
|
|
|
if (!latentChildWindows)
|
|
|
|
latentChildWindows = [[NSMutableArray alloc] init];
|
|
|
|
if (![latentChildWindows containsObject:child])
|
|
|
|
[latentChildWindows addObject:child];
|
2013-08-30 07:00:49 +02:00
|
|
|
child.latentParentWindow = self;
|
2013-08-30 07:00:56 +02:00
|
|
|
}
|
2013-08-30 07:00:49 +02:00
|
|
|
|
|
|
|
return reordered;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) addChildWineWindow:(WineWindow*)child
|
|
|
|
{
|
|
|
|
return [self addChildWineWindow:child assumeVisible:FALSE];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeChildWineWindow:(WineWindow*)child
|
|
|
|
{
|
|
|
|
[self removeChildWindow:child];
|
|
|
|
if (child.latentParentWindow == self)
|
|
|
|
child.latentParentWindow = nil;
|
2013-08-30 07:00:56 +02:00
|
|
|
[latentChildWindows removeObjectIdenticalTo:child];
|
2013-08-30 07:00:49 +02:00
|
|
|
}
|
|
|
|
|
2017-04-24 20:07:11 +02:00
|
|
|
- (void) setChildWineWindows:(NSArray*)childWindows
|
|
|
|
{
|
|
|
|
NSArray* origChildren;
|
|
|
|
NSUInteger count, start, limit, i;
|
|
|
|
|
|
|
|
origChildren = self.childWineWindows;
|
|
|
|
|
|
|
|
// If the current and desired children arrays match up to a point, leave
|
|
|
|
// those matching children alone.
|
|
|
|
count = childWindows.count;
|
|
|
|
limit = MIN(origChildren.count, count);
|
|
|
|
for (start = 0; start < limit; start++)
|
|
|
|
{
|
|
|
|
if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all of the child windows and re-add them back-to-front so they
|
|
|
|
// are in the desired order.
|
|
|
|
for (i = start; i < count; i++)
|
|
|
|
{
|
|
|
|
WineWindow* child = [childWindows objectAtIndex:i];
|
|
|
|
[self removeChildWindow:child];
|
|
|
|
}
|
|
|
|
for (i = start; i < count; i++)
|
|
|
|
{
|
|
|
|
WineWindow* child = [childWindows objectAtIndex:i];
|
|
|
|
[self addChildWindow:child ordered:NSWindowAbove];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
|
|
|
|
{
|
|
|
|
NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
|
|
|
|
NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
|
|
|
|
NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
|
|
|
|
NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
|
|
|
|
if (index1 == NSNotFound)
|
|
|
|
{
|
|
|
|
if (index2 == NSNotFound)
|
|
|
|
return NSOrderedSame;
|
|
|
|
else
|
|
|
|
return NSOrderedAscending;
|
|
|
|
}
|
|
|
|
else if (index2 == NSNotFound)
|
|
|
|
return NSOrderedDescending;
|
|
|
|
else if (index1 < index2)
|
|
|
|
return NSOrderedDescending;
|
|
|
|
else if (index2 < index1)
|
|
|
|
return NSOrderedAscending;
|
|
|
|
|
|
|
|
return NSOrderedSame;
|
|
|
|
}
|
|
|
|
|
2013-08-30 07:00:49 +02:00
|
|
|
- (BOOL) becameEligibleParentOrChild
|
|
|
|
{
|
2013-08-30 07:00:56 +02:00
|
|
|
BOOL reordered = FALSE;
|
|
|
|
NSUInteger count;
|
|
|
|
|
2013-09-03 05:48:59 +02:00
|
|
|
if (latentParentWindow.floating || !self.floating)
|
|
|
|
{
|
|
|
|
// If we aren't visible currently, we assume that we should be and soon
|
|
|
|
// will be. So, if the latent parent is visible that's enough to assume
|
|
|
|
// we can establish the parent-child relationship in Cocoa. That will
|
|
|
|
// actually make us visible, which is fine.
|
|
|
|
if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
|
|
|
|
reordered = TRUE;
|
|
|
|
}
|
2013-08-30 07:00:56 +02:00
|
|
|
|
|
|
|
// Here, though, we may not actually be visible yet and adding a child
|
|
|
|
// won't make us visible. The caller will have to call this method
|
|
|
|
// again after actually making us visible.
|
|
|
|
if ([self isVisible] && (count = [latentChildWindows count]))
|
|
|
|
{
|
2017-04-24 20:07:11 +02:00
|
|
|
NSMutableArray* windowNumbers;
|
|
|
|
NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
|
2013-08-30 07:00:56 +02:00
|
|
|
NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
|
|
|
|
NSUInteger i;
|
|
|
|
|
2017-04-24 20:07:11 +02:00
|
|
|
windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
|
|
|
|
|
2013-08-30 07:00:56 +02:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
WineWindow* child = [latentChildWindows objectAtIndex:i];
|
2013-09-03 05:48:59 +02:00
|
|
|
if ([child isVisible] && (self.floating || !child.floating))
|
2013-08-30 07:00:56 +02:00
|
|
|
{
|
|
|
|
if (child.latentParentWindow == self)
|
|
|
|
{
|
|
|
|
if ([self level] > [child level])
|
|
|
|
[child setLevel:[self level]];
|
2017-04-24 20:07:11 +02:00
|
|
|
[childWindows addObject:child];
|
2013-08-30 07:00:56 +02:00
|
|
|
child.latentParentWindow = nil;
|
|
|
|
reordered = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
|
|
|
|
[indexesToRemove addIndex:i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[latentChildWindows removeObjectsAtIndexes:indexesToRemove];
|
2017-04-24 20:07:11 +02:00
|
|
|
|
|
|
|
[childWindows sortWithOptions:NSSortStable
|
|
|
|
usingComparator:^NSComparisonResult(id obj1, id obj2){
|
|
|
|
return compare_windows_back_to_front(obj1, obj2, windowNumbers);
|
|
|
|
}];
|
|
|
|
[self setChildWineWindows:childWindows];
|
2013-08-30 07:00:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return reordered;
|
2013-08-30 07:00:49 +02:00
|
|
|
}
|
|
|
|
|
2013-09-03 05:48:59 +02:00
|
|
|
- (void) becameIneligibleChild
|
2013-08-30 07:00:49 +02:00
|
|
|
{
|
|
|
|
WineWindow* parent = (WineWindow*)[self parentWindow];
|
|
|
|
if (parent)
|
|
|
|
{
|
2013-08-30 07:00:56 +02:00
|
|
|
if (!parent->latentChildWindows)
|
|
|
|
parent->latentChildWindows = [[NSMutableArray alloc] init];
|
|
|
|
[parent->latentChildWindows insertObject:self atIndex:0];
|
2013-08-30 07:00:49 +02:00
|
|
|
self.latentParentWindow = parent;
|
|
|
|
[parent removeChildWindow:self];
|
|
|
|
}
|
2013-09-03 05:48:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) becameIneligibleParentOrChild
|
|
|
|
{
|
2014-12-17 15:59:09 +01:00
|
|
|
NSArray* childWindows = [self childWineWindows];
|
2013-09-03 05:48:59 +02:00
|
|
|
|
|
|
|
[self becameIneligibleChild];
|
2013-08-30 07:00:53 +02:00
|
|
|
|
|
|
|
if ([childWindows count])
|
|
|
|
{
|
|
|
|
WineWindow* child;
|
2013-08-30 07:00:56 +02:00
|
|
|
|
|
|
|
for (child in childWindows)
|
2013-08-30 07:00:53 +02:00
|
|
|
{
|
|
|
|
child.latentParentWindow = self;
|
|
|
|
[self removeChildWindow:child];
|
|
|
|
}
|
2013-08-30 07:00:56 +02:00
|
|
|
|
|
|
|
if (latentChildWindows)
|
|
|
|
[latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
|
|
|
|
else
|
|
|
|
latentChildWindows = [childWindows mutableCopy];
|
2013-08-30 07:00:53 +02:00
|
|
|
}
|
2013-08-30 07:00:49 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 01:43:44 +02:00
|
|
|
// Determine if, among Wine windows, this window is directly above or below
|
|
|
|
// a given other Wine window with no other Wine window intervening.
|
|
|
|
// Intervening non-Wine windows are ignored.
|
|
|
|
- (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
|
|
|
|
{
|
|
|
|
NSNumber* windowNumber;
|
|
|
|
NSNumber* otherWindowNumber;
|
|
|
|
NSArray* windowNumbers;
|
|
|
|
NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
|
|
|
|
|
|
|
|
if (![self isVisible] || ![otherWindow isVisible])
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
|
|
|
|
otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
|
|
|
|
windowNumbers = [[self class] windowNumbersWithOptions:0];
|
|
|
|
windowIndex = [windowNumbers indexOfObject:windowNumber];
|
|
|
|
otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
|
|
|
|
|
|
|
|
if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (orderingMode == NSWindowAbove)
|
|
|
|
{
|
|
|
|
lowIndex = windowIndex;
|
|
|
|
highIndex = otherWindowIndex;
|
|
|
|
}
|
|
|
|
else if (orderingMode == NSWindowBelow)
|
|
|
|
{
|
|
|
|
lowIndex = otherWindowIndex;
|
|
|
|
highIndex = windowIndex;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (highIndex <= lowIndex)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
for (i = lowIndex + 1; i < highIndex; i++)
|
|
|
|
{
|
|
|
|
NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
|
|
|
|
NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
|
|
|
|
if ([interveningWindow isKindOfClass:[WineWindow class]])
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-06-07 11:26:02 +02:00
|
|
|
- (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
|
|
|
|
{
|
|
|
|
NSMutableArray* windowNumbers;
|
|
|
|
NSNumber* childWindowNumber;
|
2017-04-24 20:07:11 +02:00
|
|
|
NSUInteger otherIndex;
|
2013-09-03 05:49:05 +02:00
|
|
|
NSArray* origChildren;
|
2013-06-07 11:26:02 +02:00
|
|
|
NSMutableArray* children;
|
|
|
|
|
|
|
|
// Get the z-order from the window server and modify it to reflect the
|
|
|
|
// requested window ordering.
|
2013-06-28 05:14:08 +02:00
|
|
|
windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
|
2013-06-07 11:26:02 +02:00
|
|
|
childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
|
|
|
|
[windowNumbers removeObject:childWindowNumber];
|
2017-04-24 20:07:12 +02:00
|
|
|
if (other)
|
|
|
|
{
|
|
|
|
otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
|
|
|
|
[windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
|
|
|
|
}
|
|
|
|
else if (mode == NSWindowAbove)
|
|
|
|
[windowNumbers insertObject:childWindowNumber atIndex:0];
|
|
|
|
else
|
|
|
|
[windowNumbers addObject:childWindowNumber];
|
2013-06-07 11:26:02 +02:00
|
|
|
|
|
|
|
// Get our child windows and sort them in the reverse of the desired
|
|
|
|
// z-order (back-to-front).
|
2014-12-17 15:59:09 +01:00
|
|
|
origChildren = [self childWineWindows];
|
2013-09-03 05:49:05 +02:00
|
|
|
children = [[origChildren mutableCopy] autorelease];
|
2013-06-07 11:26:02 +02:00
|
|
|
[children sortWithOptions:NSSortStable
|
|
|
|
usingComparator:^NSComparisonResult(id obj1, id obj2){
|
2017-04-24 20:07:11 +02:00
|
|
|
return compare_windows_back_to_front(obj1, obj2, windowNumbers);
|
2013-06-07 11:26:02 +02:00
|
|
|
}];
|
|
|
|
|
2017-04-24 20:07:11 +02:00
|
|
|
[self setChildWineWindows:children];
|
2013-06-07 11:26:02 +02:00
|
|
|
}
|
|
|
|
|
2017-04-24 20:07:12 +02:00
|
|
|
// Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
|
|
|
|
// There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
|
|
|
|
// (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
|
|
|
|
// if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
|
|
|
|
// otherwise, the two ancestors are each roots of disjoint window trees
|
|
|
|
// (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
|
|
|
|
// (non-nil, nil) self is a descendent of other and *ancestor is the direct child
|
|
|
|
- (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
|
|
|
|
{
|
|
|
|
NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
|
|
|
|
WineWindow* child;
|
|
|
|
WineWindow* parent;
|
|
|
|
for (child = other;
|
|
|
|
(parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
|
|
|
|
child = parent)
|
|
|
|
{
|
|
|
|
if (parent == self)
|
|
|
|
{
|
|
|
|
*ancestor = nil;
|
|
|
|
*ancestorOfOther = child;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
[otherAncestors addObject:parent];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (child = self;
|
|
|
|
(parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
|
|
|
|
child = parent)
|
|
|
|
{
|
|
|
|
NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
|
|
|
|
if (index != NSNotFound)
|
|
|
|
{
|
|
|
|
*ancestor = child;
|
|
|
|
if (index == 0)
|
|
|
|
*ancestorOfOther = nil;
|
|
|
|
else
|
|
|
|
*ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ancestor = child;
|
|
|
|
*ancestorOfOther = otherAncestors.lastObject;;
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
/* Returns whether or not the window was ordered in, which depends on if
|
|
|
|
its frame intersects any screen. */
|
2013-11-15 03:52:04 +01:00
|
|
|
- (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
2013-04-16 07:38:01 +02:00
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
2013-11-15 03:52:04 +01:00
|
|
|
if (![self isMiniaturized])
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
2013-05-17 01:43:44 +02:00
|
|
|
BOOL needAdjustWindowLevels = FALSE;
|
2013-12-31 08:05:13 +01:00
|
|
|
BOOL wasVisible;
|
2017-04-24 20:07:12 +02:00
|
|
|
WineWindow* parent;
|
|
|
|
WineWindow* child;
|
2013-05-17 01:43:44 +02:00
|
|
|
|
2013-04-16 07:38:01 +02:00
|
|
|
[controller transformProcessToForeground];
|
2016-07-21 01:50:25 +02:00
|
|
|
if ([NSApp isHidden])
|
|
|
|
[NSApp unhide:nil];
|
2013-12-31 08:05:13 +01:00
|
|
|
wasVisible = [self isVisible];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-06-20 02:09:18 +02:00
|
|
|
if (activate)
|
|
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
NSDisableScreenUpdates();
|
|
|
|
|
2013-08-30 07:00:49 +02:00
|
|
|
if ([self becameEligibleParentOrChild])
|
2013-05-17 01:43:44 +02:00
|
|
|
needAdjustWindowLevels = TRUE;
|
2013-08-30 07:00:49 +02:00
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
if (prev || next)
|
2013-02-18 02:28:27 +01:00
|
|
|
{
|
2013-05-17 01:43:33 +02:00
|
|
|
WineWindow* other = [prev isVisible] ? prev : next;
|
|
|
|
NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
|
|
|
|
|
2013-05-17 01:43:44 +02:00
|
|
|
if (![self isOrdered:orderingMode relativeTo:other])
|
|
|
|
{
|
2017-04-24 20:07:12 +02:00
|
|
|
WineWindow* ancestor;
|
|
|
|
WineWindow* ancestorOfOther;
|
|
|
|
|
|
|
|
[self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
|
|
|
|
if (ancestor)
|
|
|
|
{
|
|
|
|
[self setAutodisplay:YES];
|
|
|
|
if (ancestorOfOther)
|
|
|
|
{
|
|
|
|
// This window level may not be right for this window based
|
|
|
|
// on floating-ness, fullscreen-ness, etc. But we set it
|
|
|
|
// temporarily to allow us to order the windows properly.
|
|
|
|
// Then the levels get fixed by -adjustWindowLevels.
|
|
|
|
if ([ancestor level] != [ancestorOfOther level])
|
|
|
|
[ancestor setLevel:[ancestorOfOther level]];
|
|
|
|
|
|
|
|
parent = (WineWindow*)ancestor.parentWindow;
|
|
|
|
if ([parent isKindOfClass:[WineWindow class]])
|
|
|
|
[parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
|
|
|
|
else
|
|
|
|
[ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
|
|
|
|
}
|
|
|
|
|
2018-05-21 20:39:02 +02:00
|
|
|
if (!ancestorOfOther || ancestor != self)
|
2017-04-24 20:07:12 +02:00
|
|
|
{
|
2018-05-21 20:39:02 +02:00
|
|
|
for (child = self;
|
|
|
|
(parent = (WineWindow*)child.parentWindow);
|
|
|
|
child = parent)
|
|
|
|
{
|
|
|
|
if ([parent isKindOfClass:[WineWindow class]])
|
|
|
|
[parent order:-orderingMode childWindow:child relativeTo:nil];
|
|
|
|
if (parent == ancestor)
|
|
|
|
break;
|
|
|
|
}
|
2017-04-24 20:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
[self checkWineDisplayLink];
|
|
|
|
needAdjustWindowLevels = TRUE;
|
|
|
|
}
|
2013-05-17 01:43:44 +02:00
|
|
|
}
|
2013-02-18 02:28:27 +01:00
|
|
|
}
|
2013-01-07 21:44:44 +01:00
|
|
|
else
|
2013-02-18 02:28:27 +01:00
|
|
|
{
|
2017-04-24 20:07:12 +02:00
|
|
|
for (child = self;
|
|
|
|
(parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
|
|
|
|
child = parent)
|
|
|
|
{
|
|
|
|
[parent order:NSWindowAbove childWindow:child relativeTo:nil];
|
|
|
|
}
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
// Again, temporarily set level to make sure we can order to
|
|
|
|
// the right place.
|
|
|
|
next = [controller frontWineWindow];
|
|
|
|
if (next && [self level] < [next level])
|
|
|
|
[self setLevel:[next level]];
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
[self setAutodisplay:YES];
|
2013-05-17 01:43:33 +02:00
|
|
|
[self orderFront:nil];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[self checkWineDisplayLink];
|
2013-05-17 01:43:44 +02:00
|
|
|
needAdjustWindowLevels = TRUE;
|
2013-02-18 02:28:27 +01:00
|
|
|
}
|
2017-04-24 20:07:13 +02:00
|
|
|
pendingOrderOut = FALSE;
|
2013-08-30 07:00:56 +02:00
|
|
|
|
|
|
|
if ([self becameEligibleParentOrChild])
|
|
|
|
needAdjustWindowLevels = TRUE;
|
|
|
|
|
2013-05-17 01:43:44 +02:00
|
|
|
if (needAdjustWindowLevels)
|
2013-06-07 00:43:06 +02:00
|
|
|
{
|
|
|
|
if (!wasVisible && fullscreen && [self isOnActiveSpace])
|
|
|
|
[controller updateFullscreenWindows];
|
2013-05-17 01:43:44 +02:00
|
|
|
[controller adjustWindowLevels];
|
2013-06-07 00:43:06 +02:00
|
|
|
}
|
2013-05-17 01:43:33 +02:00
|
|
|
|
2013-06-04 11:59:42 +02:00
|
|
|
if (pendingMinimize)
|
|
|
|
{
|
2013-09-30 17:29:23 +02:00
|
|
|
[super miniaturize:nil];
|
2013-06-04 11:59:42 +02:00
|
|
|
pendingMinimize = FALSE;
|
|
|
|
}
|
|
|
|
|
2013-05-17 01:43:33 +02:00
|
|
|
NSEnableScreenUpdates();
|
2013-01-11 13:21:06 +01:00
|
|
|
|
2013-01-27 23:19:29 +01:00
|
|
|
/* Cocoa may adjust the frame when the window is ordered onto the screen.
|
|
|
|
Generate a frame-changed event just in case. The back end will ignore
|
|
|
|
it if nothing actually changed. */
|
|
|
|
[self windowDidResize:nil];
|
|
|
|
|
2013-01-11 13:21:06 +01:00
|
|
|
if (![self isExcludedFromWindowsMenu])
|
|
|
|
[NSApp addWindowsItem:self title:[self title] filename:NO];
|
2013-01-07 21:44:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:19:36 +01:00
|
|
|
- (void) doOrderOut
|
|
|
|
{
|
2013-06-07 00:43:06 +02:00
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
|
|
|
BOOL wasVisible = [self isVisible];
|
|
|
|
BOOL wasOnActiveSpace = [self isOnActiveSpace];
|
|
|
|
|
2017-06-21 17:15:50 +02:00
|
|
|
[self endWindowDragging];
|
|
|
|
[controller windowWillOrderOut:self];
|
|
|
|
|
2017-04-24 20:07:13 +02:00
|
|
|
if (enteringFullScreen || exitingFullScreen)
|
|
|
|
{
|
|
|
|
pendingOrderOut = TRUE;
|
|
|
|
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
|
|
|
|
event_mask_for_type(WINDOW_GOT_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_LOST_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
|
|
|
|
event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
|
|
|
|
event_mask_for_type(WINDOW_RESTORE_REQUESTED)
|
|
|
|
forWindow:self];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pendingOrderOut = FALSE;
|
|
|
|
|
2013-06-04 11:59:42 +02:00
|
|
|
if ([self isMiniaturized])
|
|
|
|
pendingMinimize = TRUE;
|
2013-08-30 07:00:49 +02:00
|
|
|
|
2015-10-23 09:48:34 +02:00
|
|
|
WineWindow* parent = (WineWindow*)self.parentWindow;
|
|
|
|
if ([parent isKindOfClass:[WineWindow class]])
|
|
|
|
[parent grabDockIconSnapshotFromWindow:self force:NO];
|
|
|
|
|
2013-08-30 07:00:49 +02:00
|
|
|
[self becameIneligibleParentOrChild];
|
2017-04-24 20:07:14 +02:00
|
|
|
if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
|
2013-09-27 06:46:31 +02:00
|
|
|
{
|
|
|
|
fakingClose = TRUE;
|
|
|
|
[self close];
|
|
|
|
fakingClose = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
[self orderOut:nil];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[self checkWineDisplayLink];
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
[self setBackgroundColor:[NSColor clearColor]];
|
|
|
|
[self setOpaque:NO];
|
|
|
|
drawnSinceShown = NO;
|
2013-12-31 08:05:09 +01:00
|
|
|
savedVisibleState = FALSE;
|
2013-06-07 00:43:06 +02:00
|
|
|
if (wasVisible && wasOnActiveSpace && fullscreen)
|
|
|
|
[controller updateFullscreenWindows];
|
|
|
|
[controller adjustWindowLevels];
|
2013-01-11 13:21:06 +01:00
|
|
|
[NSApp removeWindowsItem:self];
|
2013-12-31 08:05:18 +01:00
|
|
|
|
|
|
|
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
|
|
|
|
event_mask_for_type(WINDOW_GOT_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_LOST_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
|
|
|
|
event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
|
|
|
|
event_mask_for_type(WINDOW_RESTORE_REQUESTED)
|
|
|
|
forWindow:self];
|
2013-01-11 13:19:36 +01:00
|
|
|
}
|
|
|
|
|
2013-06-07 00:43:06 +02:00
|
|
|
- (void) updateFullscreen
|
|
|
|
{
|
2016-05-05 20:53:36 +02:00
|
|
|
NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
|
2013-10-10 21:22:08 +02:00
|
|
|
BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
|
2013-06-07 00:43:06 +02:00
|
|
|
|
|
|
|
if (nowFullscreen != fullscreen)
|
|
|
|
{
|
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
|
|
|
|
|
|
|
fullscreen = nowFullscreen;
|
|
|
|
if ([self isVisible] && [self isOnActiveSpace])
|
|
|
|
[controller updateFullscreenWindows];
|
|
|
|
|
|
|
|
[controller adjustWindowLevels];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
- (void) setFrameAndWineFrame:(NSRect)frame
|
|
|
|
{
|
|
|
|
[self setFrame:frame display:YES];
|
|
|
|
|
|
|
|
wineFrame = frame;
|
|
|
|
roundedWineFrame = self.frame;
|
|
|
|
CGFloat junk;
|
|
|
|
#if CGFLOAT_IS_DOUBLE
|
|
|
|
if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
|
|
|
|
!modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
|
|
|
|
fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
|
|
|
|
fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
|
|
|
|
fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
|
|
|
|
fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
|
|
|
|
roundedWineFrame = wineFrame;
|
|
|
|
#else
|
|
|
|
if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
|
|
|
|
!modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
|
|
|
|
fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
|
|
|
|
fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
|
|
|
|
fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
|
|
|
|
fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
|
|
|
|
roundedWineFrame = wineFrame;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-11-15 03:52:04 +01:00
|
|
|
- (void) setFrameFromWine:(NSRect)contentRect
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
|
|
|
/* Origin is (left, top) in a top-down space. Need to convert it to
|
|
|
|
(left, bottom) in a bottom-up space. */
|
2013-04-16 07:38:01 +02:00
|
|
|
[[WineApplicationController sharedController] flipRect:&contentRect];
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-05-17 01:43:37 +02:00
|
|
|
/* The back end is establishing a new window size and position. It's
|
|
|
|
not interested in any stale events regarding those that may be sitting
|
|
|
|
in the queue. */
|
|
|
|
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
|
|
|
|
forWindow:self];
|
|
|
|
|
2013-02-18 02:28:19 +01:00
|
|
|
if (!NSIsEmptyRect(contentRect))
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
2013-05-17 01:43:37 +02:00
|
|
|
NSRect frame, oldFrame;
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
oldFrame = self.wine_fractionalFrame;
|
2013-02-18 02:28:19 +01:00
|
|
|
frame = [self frameRectForContentRect:contentRect];
|
|
|
|
if (!NSEqualRects(frame, oldFrame))
|
|
|
|
{
|
2013-11-15 03:52:07 +01:00
|
|
|
BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
|
|
|
|
BOOL needEnableScreenUpdates = FALSE;
|
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
if ([self preventResizing])
|
2013-12-30 04:33:36 +01:00
|
|
|
{
|
|
|
|
// Allow the following calls to -setFrame:display: to work even
|
|
|
|
// if they would violate the content size constraints. This
|
|
|
|
// shouldn't be necessary since the content size constraints are
|
|
|
|
// documented to not constrain that method, but it seems to be.
|
|
|
|
[self setContentMinSize:NSZeroSize];
|
|
|
|
[self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
|
|
|
|
}
|
|
|
|
|
2014-12-17 15:59:09 +01:00
|
|
|
if (equalSizes && [[self childWineWindows] count])
|
2013-07-02 08:24:19 +02:00
|
|
|
{
|
2013-11-15 03:52:07 +01:00
|
|
|
// If we change the window frame such that the origin moves
|
|
|
|
// but the size doesn't change, then Cocoa moves child
|
|
|
|
// windows with the parent. We don't want that so we fake
|
|
|
|
// a change of the size and then change it back.
|
|
|
|
NSRect bogusFrame = frame;
|
|
|
|
bogusFrame.size.width++;
|
|
|
|
|
|
|
|
NSDisableScreenUpdates();
|
|
|
|
needEnableScreenUpdates = TRUE;
|
|
|
|
|
|
|
|
ignore_windowResize = TRUE;
|
|
|
|
[self setFrame:bogusFrame display:NO];
|
|
|
|
ignore_windowResize = FALSE;
|
2013-07-02 08:24:19 +02:00
|
|
|
}
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
[self setFrameAndWineFrame:frame];
|
2013-12-30 04:34:48 +01:00
|
|
|
if ([self preventResizing])
|
2013-12-30 04:33:36 +01:00
|
|
|
{
|
|
|
|
[self setContentMinSize:contentRect.size];
|
|
|
|
[self setContentMaxSize:contentRect.size];
|
|
|
|
}
|
2013-11-15 03:52:07 +01:00
|
|
|
|
|
|
|
if (needEnableScreenUpdates)
|
|
|
|
NSEnableScreenUpdates();
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
if (!enteringFullScreen &&
|
|
|
|
[[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
|
|
|
|
nonFullscreenFrame = frame;
|
|
|
|
|
2013-06-07 00:43:06 +02:00
|
|
|
[self updateFullscreen];
|
|
|
|
|
2013-11-15 03:52:04 +01:00
|
|
|
if ([self isOrderedIn])
|
2013-05-17 01:43:37 +02:00
|
|
|
{
|
|
|
|
/* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
|
|
|
|
event. The back end will ignore it if nothing actually changed. */
|
|
|
|
[self windowDidResize:nil];
|
|
|
|
}
|
|
|
|
}
|
2013-02-18 02:28:16 +01:00
|
|
|
}
|
2013-01-07 21:44:44 +01:00
|
|
|
}
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
- (NSRect) wine_fractionalFrame
|
|
|
|
{
|
|
|
|
NSRect frame = self.frame;
|
|
|
|
if (NSEqualRects(frame, roundedWineFrame))
|
|
|
|
frame = wineFrame;
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:19:36 +01:00
|
|
|
- (void) setMacDrvParentWindow:(WineWindow*)parent
|
|
|
|
{
|
2013-08-30 07:00:46 +02:00
|
|
|
WineWindow* oldParent = (WineWindow*)[self parentWindow];
|
|
|
|
if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
|
2013-01-11 13:19:36 +01:00
|
|
|
{
|
2013-08-30 07:00:49 +02:00
|
|
|
[oldParent removeChildWineWindow:self];
|
|
|
|
[latentParentWindow removeChildWineWindow:self];
|
|
|
|
if ([parent addChildWineWindow:self])
|
2013-05-17 01:43:33 +02:00
|
|
|
[[WineApplicationController sharedController] adjustWindowLevels];
|
2017-04-24 20:07:15 +02:00
|
|
|
[self adjustFullScreenBehavior:[self collectionBehavior]];
|
2013-01-11 13:19:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:18:05 +01:00
|
|
|
- (void) setDisabled:(BOOL)newValue
|
|
|
|
{
|
|
|
|
if (disabled != newValue)
|
|
|
|
{
|
|
|
|
disabled = newValue;
|
|
|
|
[self adjustFeaturesForState];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-15 03:23:58 +01:00
|
|
|
- (BOOL) needsTransparency
|
|
|
|
{
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
|
2016-05-13 01:50:39 +02:00
|
|
|
(gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
|
2013-01-15 03:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkTransparency
|
|
|
|
{
|
|
|
|
if (![self isOpaque] && !self.needsTransparency)
|
|
|
|
{
|
2014-10-03 00:06:14 +02:00
|
|
|
self.shapeChangedSinceLastDraw = TRUE;
|
|
|
|
[[self contentView] setNeedsDisplay:YES];
|
2013-01-15 03:23:58 +01:00
|
|
|
[self setBackgroundColor:[NSColor windowBackgroundColor]];
|
|
|
|
[self setOpaque:YES];
|
|
|
|
}
|
|
|
|
else if ([self isOpaque] && self.needsTransparency)
|
|
|
|
{
|
2014-10-03 00:06:14 +02:00
|
|
|
self.shapeChangedSinceLastDraw = TRUE;
|
|
|
|
[[self contentView] setNeedsDisplay:YES];
|
2013-01-15 03:23:58 +01:00
|
|
|
[self setBackgroundColor:[NSColor clearColor]];
|
|
|
|
[self setOpaque:NO];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setShape:(NSBezierPath*)newShape
|
|
|
|
{
|
|
|
|
if (shape == newShape) return;
|
|
|
|
|
|
|
|
if (shape)
|
|
|
|
{
|
|
|
|
[[self contentView] setNeedsDisplayInRect:[shape bounds]];
|
|
|
|
[shape release];
|
|
|
|
}
|
|
|
|
if (newShape)
|
|
|
|
[[self contentView] setNeedsDisplayInRect:[newShape bounds]];
|
|
|
|
|
|
|
|
shape = [newShape copy];
|
|
|
|
self.shapeChangedSinceLastDraw = TRUE;
|
|
|
|
|
|
|
|
[self checkTransparency];
|
|
|
|
}
|
|
|
|
|
2013-04-24 23:10:10 +02:00
|
|
|
- (void) makeFocused:(BOOL)activate
|
2013-01-27 23:19:41 +01:00
|
|
|
{
|
2013-12-11 19:50:17 +01:00
|
|
|
if (activate)
|
|
|
|
{
|
|
|
|
[[WineApplicationController sharedController] transformProcessToForeground];
|
|
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
|
|
}
|
2013-05-17 01:43:33 +02:00
|
|
|
|
2013-09-25 22:10:57 +02:00
|
|
|
causing_becomeKeyWindow = self;
|
2013-05-17 01:43:31 +02:00
|
|
|
[self makeKeyWindow];
|
2013-09-25 22:10:57 +02:00
|
|
|
causing_becomeKeyWindow = nil;
|
2013-12-31 08:05:18 +01:00
|
|
|
|
|
|
|
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
|
|
|
|
event_mask_for_type(WINDOW_LOST_FOCUS)
|
|
|
|
forWindow:self];
|
2013-01-27 23:19:41 +01:00
|
|
|
}
|
|
|
|
|
2013-02-04 00:20:18 +01:00
|
|
|
- (void) postKey:(uint16_t)keyCode
|
|
|
|
pressed:(BOOL)pressed
|
|
|
|
modifiers:(NSUInteger)modifiers
|
|
|
|
event:(NSEvent*)theEvent
|
|
|
|
{
|
2013-04-04 01:56:35 +02:00
|
|
|
macdrv_event* event;
|
2013-02-04 00:20:18 +01:00
|
|
|
CGEventRef cgevent;
|
2013-04-16 07:38:01 +02:00
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
2013-02-04 00:20:18 +01:00
|
|
|
|
2013-04-04 01:56:35 +02:00
|
|
|
event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
|
|
|
|
event->key.keycode = keyCode;
|
|
|
|
event->key.modifiers = modifiers;
|
2013-04-16 07:38:01 +02:00
|
|
|
event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
|
2013-02-04 00:20:18 +01:00
|
|
|
|
|
|
|
if ((cgevent = [theEvent CGEvent]))
|
2017-05-16 00:17:46 +02:00
|
|
|
controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
|
2013-02-04 00:20:18 +01:00
|
|
|
|
2013-04-04 01:56:35 +02:00
|
|
|
[queue postEvent:event];
|
|
|
|
|
|
|
|
macdrv_release_event(event);
|
2013-07-09 09:49:59 +02:00
|
|
|
|
|
|
|
[controller noteKey:keyCode pressed:pressed];
|
2013-02-04 00:20:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) postKeyEvent:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
[self flagsChanged:theEvent];
|
|
|
|
[self postKey:[theEvent keyCode]
|
|
|
|
pressed:[theEvent type] == NSKeyDown
|
2018-09-12 11:35:30 +02:00
|
|
|
modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
|
2013-02-04 00:20:18 +01:00
|
|
|
event:theEvent];
|
|
|
|
}
|
|
|
|
|
2013-09-18 20:00:37 +02:00
|
|
|
- (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
|
|
|
|
{
|
|
|
|
savedContentMinSize = minSize;
|
|
|
|
savedContentMaxSize = maxSize;
|
2013-12-30 04:34:48 +01:00
|
|
|
if (![self preventResizing])
|
2013-09-18 20:00:37 +02:00
|
|
|
{
|
|
|
|
[self setContentMinSize:minSize];
|
|
|
|
[self setContentMaxSize:maxSize];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-08 09:21:34 +02:00
|
|
|
- (WineWindow*) ancestorWineWindow
|
|
|
|
{
|
|
|
|
WineWindow* ancestor = self;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
WineWindow* parent = (WineWindow*)[ancestor parentWindow];
|
|
|
|
if ([parent isKindOfClass:[WineWindow class]])
|
|
|
|
ancestor = parent;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ancestor;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) postBroughtForwardEvent
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
|
|
|
|
2016-10-11 05:38:13 +02:00
|
|
|
- (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
|
|
|
|
{
|
|
|
|
macdrv_event* event;
|
|
|
|
NSUInteger style = self.styleMask;
|
|
|
|
|
|
|
|
if (isFullscreen)
|
|
|
|
style |= NSFullScreenWindowMask;
|
|
|
|
else
|
|
|
|
style &= ~NSFullScreenWindowMask;
|
|
|
|
frame = [[self class] contentRectForFrameRect:frame styleMask:style];
|
|
|
|
[[WineApplicationController sharedController] flipRect:&frame];
|
|
|
|
|
|
|
|
/* Coalesce events by discarding any previous ones still in the queue. */
|
|
|
|
[queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
|
|
|
|
forWindow:self];
|
|
|
|
|
|
|
|
event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
|
|
|
|
event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
|
|
|
|
event->window_frame_changed.fullscreen = isFullscreen;
|
|
|
|
event->window_frame_changed.in_resize = resizing;
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
|
|
|
|
2014-04-28 22:09:28 +02:00
|
|
|
- (void) updateForCursorClipping
|
|
|
|
{
|
|
|
|
[self adjustFeaturesForState];
|
|
|
|
}
|
|
|
|
|
2015-03-24 00:58:11 +01:00
|
|
|
- (void) endWindowDragging
|
|
|
|
{
|
|
|
|
if (draggingPhase)
|
|
|
|
{
|
|
|
|
if (draggingPhase == 3)
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
draggingPhase = 0;
|
|
|
|
[[WineApplicationController sharedController] window:self isBeingDragged:NO];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 03:31:22 +01:00
|
|
|
- (NSMutableDictionary*) displayIDToDisplayLinkMap
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
{
|
|
|
|
static NSMutableDictionary* displayIDToDisplayLinkMap;
|
|
|
|
if (!displayIDToDisplayLinkMap)
|
|
|
|
{
|
|
|
|
displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
|
|
|
|
object:NSApp
|
|
|
|
queue:nil
|
|
|
|
usingBlock:^(NSNotification *note){
|
|
|
|
NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
|
|
|
|
NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
|
|
|
|
[badDisplayIDs minusSet:validDisplayIDs];
|
|
|
|
[displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
|
|
|
|
}];
|
|
|
|
}
|
2015-11-10 03:31:22 +01:00
|
|
|
return displayIDToDisplayLinkMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (WineDisplayLink*) wineDisplayLink
|
|
|
|
{
|
|
|
|
if (!_lastDisplayID)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
|
|
|
|
return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkWineDisplayLink
|
|
|
|
{
|
|
|
|
NSScreen* screen = self.screen;
|
|
|
|
if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
|
|
|
|
screen = nil;
|
|
|
|
#if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
|
|
|
|
if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
|
|
|
|
screen = nil;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
|
|
|
|
CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
|
|
|
|
if (displayID == _lastDisplayID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
|
|
|
|
if (_lastDisplayID)
|
|
|
|
{
|
|
|
|
WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
|
|
|
|
[link removeWindow:self];
|
|
|
|
}
|
|
|
|
if (displayID)
|
|
|
|
{
|
|
|
|
WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
|
|
|
|
if (!link)
|
|
|
|
{
|
|
|
|
link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
|
|
|
|
[displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
|
|
|
|
}
|
|
|
|
[link addWindow:self];
|
|
|
|
[self displayIfNeeded];
|
|
|
|
}
|
|
|
|
_lastDisplayID = displayID;
|
|
|
|
}
|
|
|
|
|
2015-10-23 09:48:34 +02:00
|
|
|
- (BOOL) isEmptyShaped
|
|
|
|
{
|
|
|
|
return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) canProvideSnapshot
|
|
|
|
{
|
|
|
|
return (self.windowNumber > 0 && ![self isEmptyShaped]);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
|
|
|
|
{
|
|
|
|
if (![self isEmptyShaped])
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
|
|
|
|
if (!force && now < lastDockIconSnapshot + 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (window)
|
|
|
|
{
|
|
|
|
if (![window canProvideSnapshot])
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CGFloat bestArea;
|
|
|
|
for (WineWindow* childWindow in self.childWindows)
|
|
|
|
{
|
|
|
|
if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
NSSize size = childWindow.frame.size;
|
|
|
|
CGFloat area = size.width * size.height;
|
|
|
|
if (!window || area > bestArea)
|
|
|
|
{
|
|
|
|
window = childWindow;
|
|
|
|
bestArea = area;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!window)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void* windowID = (const void*)(CGWindowID)window.windowNumber;
|
|
|
|
CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
|
|
|
|
CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
|
|
|
|
CFRelease(windowIDs);
|
|
|
|
if (!windowImage)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSImage* appImage = [NSApp applicationIconImage];
|
|
|
|
if (!appImage)
|
|
|
|
appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
|
|
|
|
|
|
|
|
NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
|
|
|
|
[dockIcon lockFocus];
|
|
|
|
|
|
|
|
CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
|
|
|
|
|
|
|
|
CGRect rect = CGRectMake(8, 8, 240, 240);
|
|
|
|
size_t width = CGImageGetWidth(windowImage);
|
|
|
|
size_t height = CGImageGetHeight(windowImage);
|
|
|
|
if (width > height)
|
|
|
|
{
|
|
|
|
rect.size.height *= height / (double)width;
|
|
|
|
rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
|
|
|
|
}
|
|
|
|
else if (width != height)
|
|
|
|
{
|
|
|
|
rect.size.width *= width / (double)height;
|
|
|
|
rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGContextDrawImage(cgcontext, rect, windowImage);
|
2015-11-10 09:29:18 +01:00
|
|
|
[appImage drawInRect:NSMakeRect(156, 4, 96, 96)
|
|
|
|
fromRect:NSZeroRect
|
|
|
|
operation:NSCompositeSourceOver
|
|
|
|
fraction:1
|
|
|
|
respectFlipped:YES
|
|
|
|
hints:nil];
|
2015-10-23 09:48:34 +02:00
|
|
|
|
|
|
|
[dockIcon unlockFocus];
|
|
|
|
|
|
|
|
CGImageRelease(windowImage);
|
|
|
|
|
|
|
|
NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
|
|
|
|
if (![imageView isKindOfClass:[NSImageView class]])
|
|
|
|
{
|
|
|
|
imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
|
|
|
|
imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
|
|
|
|
self.dockTile.contentView = imageView;
|
|
|
|
}
|
|
|
|
imageView.image = dockIcon;
|
|
|
|
[self.dockTile display];
|
|
|
|
lastDockIconSnapshot = now;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkEmptyShaped
|
|
|
|
{
|
|
|
|
if (self.dockTile.contentView && ![self isEmptyShaped])
|
|
|
|
{
|
|
|
|
self.dockTile.contentView = nil;
|
|
|
|
lastDockIconSnapshot = 0;
|
|
|
|
}
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[self checkWineDisplayLink];
|
2015-10-23 09:48:34 +02:00
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ---------- NSWindow method overrides ----------
|
|
|
|
*/
|
|
|
|
- (BOOL) canBecomeKeyWindow
|
|
|
|
{
|
2013-09-25 22:10:57 +02:00
|
|
|
if (causing_becomeKeyWindow == self) return YES;
|
2013-01-11 13:18:36 +01:00
|
|
|
if (self.disabled || self.noActivate) return NO;
|
2013-01-27 23:19:41 +01:00
|
|
|
return [self isKeyWindow];
|
2013-01-07 21:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) canBecomeMainWindow
|
|
|
|
{
|
|
|
|
return [self canBecomeKeyWindow];
|
|
|
|
}
|
|
|
|
|
2013-02-18 02:28:30 +01:00
|
|
|
- (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
|
|
|
|
{
|
|
|
|
// If a window is sized to completely cover a screen, then it's in
|
|
|
|
// full-screen mode. In that case, we don't allow NSWindow to constrain
|
|
|
|
// it.
|
2013-11-15 03:52:04 +01:00
|
|
|
NSArray* screens = [NSScreen screens];
|
2013-02-18 02:28:30 +01:00
|
|
|
NSRect contentRect = [self contentRectForFrameRect:frameRect];
|
2013-11-15 03:52:04 +01:00
|
|
|
if (!screen_covered_by_rect(contentRect, screens) &&
|
|
|
|
frame_intersects_screens(frameRect, screens))
|
2013-02-18 02:28:30 +01:00
|
|
|
frameRect = [super constrainFrameRect:frameRect toScreen:screen];
|
|
|
|
return frameRect;
|
|
|
|
}
|
|
|
|
|
2015-10-05 22:44:28 +02:00
|
|
|
// This private method of NSWindow is called as Cocoa reacts to the display
|
|
|
|
// configuration changing. Among other things, it adjusts the window's
|
|
|
|
// frame based on how the screen(s) changed size. That tells Wine that the
|
|
|
|
// window has been moved. We don't want that. Rather, we want to make
|
|
|
|
// sure that the WinAPI notion of the window position is maintained/
|
|
|
|
// restored, possibly undoing or overriding Cocoa's adjustment.
|
|
|
|
//
|
|
|
|
// So, we queue a REASSERT_WINDOW_POSITION event to the back end before
|
|
|
|
// Cocoa has a chance to adjust the frame, thus preceding any resulting
|
|
|
|
// WINDOW_FRAME_CHANGED event that may get queued. The back end will
|
|
|
|
// reassert its notion of the position. That call won't get processed
|
|
|
|
// until after this method returns, so it will override whatever this
|
|
|
|
// method does to the window position. It will also discard any pending
|
|
|
|
// WINDOW_FRAME_CHANGED events.
|
|
|
|
//
|
|
|
|
// Unfortunately, the only way I've found to know when Cocoa is _about to_
|
|
|
|
// adjust the window's position due to a display change is to hook into
|
|
|
|
// this private method. This private method has remained stable from 10.6
|
|
|
|
// through 10.11. If it does change, the most likely thing is that it
|
|
|
|
// will be removed and no longer called and this fix will simply stop
|
|
|
|
// working. The only real danger would be if Apple changed the return type
|
|
|
|
// to a struct or floating-point type, which would change the calling
|
|
|
|
// convention.
|
|
|
|
- (id) _displayChanged
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
|
|
|
|
return [super _displayChanged];
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:21:06 +01:00
|
|
|
- (BOOL) isExcludedFromWindowsMenu
|
|
|
|
{
|
|
|
|
return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) validateMenuItem:(NSMenuItem *)menuItem
|
|
|
|
{
|
2013-05-17 01:41:59 +02:00
|
|
|
BOOL ret = [super validateMenuItem:menuItem];
|
|
|
|
|
2013-01-11 13:21:06 +01:00
|
|
|
if ([menuItem action] == @selector(makeKeyAndOrderFront:))
|
2013-05-17 01:41:59 +02:00
|
|
|
ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
|
winemac: Prevent maximized windows from entering Cocoa full-screen mode.
OS X doesn't really have the concept of windows being maximized; that is, being
in a mode where they can't be moved or resized. As a consequence, it doesn't
have a button in the window title bar to restore a maximized window to normal.
So, when a Wine window is maximized, the Mac driver hijacks the green zoom
button to act as a restore button. (When a window is zoomed, the green button
"unzooms" back to its last user size and position, so it's analogous.)
However, with OS X 10.10 (Yosemite), the green button prefers to act as a
toggle for the Cocoa full-screen mode rather than zooming and unzooming. This
made it difficult for users to restore a maximized window. They would have to
Option-click the green button, double-click the title bar, or choose Zoom
from the Window menu, none of which is obvious.
The fix is to disable Cocoa full-screen mode for maximized windows. Then, the
green button reverts to unzoom and restoring the window.
2015-03-13 00:45:25 +01:00
|
|
|
if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
|
2013-10-10 21:22:08 +02:00
|
|
|
ret = NO;
|
2013-05-17 01:41:59 +02:00
|
|
|
|
|
|
|
return ret;
|
2013-01-11 13:21:06 +01:00
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:48 +01:00
|
|
|
/* We don't call this. It's the action method of the items in the Window menu. */
|
|
|
|
- (void) makeKeyAndOrderFront:(id)sender
|
|
|
|
{
|
2013-08-30 07:00:44 +02:00
|
|
|
if ([self isMiniaturized])
|
|
|
|
[self deminiaturize:nil];
|
|
|
|
[self orderBelow:nil orAbove:nil activate:NO];
|
2013-10-08 09:21:34 +02:00
|
|
|
[[self ancestorWineWindow] postBroughtForwardEvent];
|
|
|
|
|
|
|
|
if (![self isKeyWindow] && !self.disabled && !self.noActivate)
|
|
|
|
[[WineApplicationController sharedController] windowGotFocus:self];
|
2013-01-27 23:19:48 +01:00
|
|
|
}
|
|
|
|
|
2013-07-09 09:49:55 +02:00
|
|
|
- (void) sendEvent:(NSEvent*)event
|
|
|
|
{
|
2015-03-24 00:58:11 +01:00
|
|
|
NSEventType type = event.type;
|
|
|
|
|
2013-07-09 09:49:55 +02:00
|
|
|
/* NSWindow consumes certain key-down events as part of Cocoa's keyboard
|
|
|
|
interface control. For example, Control-Tab switches focus among
|
|
|
|
views. We want to bypass that feature, so directly route key-down
|
|
|
|
events to -keyDown:. */
|
2015-03-24 00:58:11 +01:00
|
|
|
if (type == NSKeyDown)
|
2013-07-09 09:49:55 +02:00
|
|
|
[[self firstResponder] keyDown:event];
|
|
|
|
else
|
2015-03-24 00:58:11 +01:00
|
|
|
{
|
|
|
|
if (!draggingPhase && maximized && ![self isMovable] &&
|
|
|
|
![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
|
|
|
|
type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
|
|
|
|
{
|
|
|
|
NSRect titleBar = self.frame;
|
|
|
|
NSRect contentRect = [self contentRectForFrameRect:titleBar];
|
|
|
|
titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
|
|
|
|
titleBar.origin.y = NSMaxY(contentRect);
|
|
|
|
|
|
|
|
dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
|
|
|
|
|
|
|
|
if (NSMouseInRect(dragStartPosition, titleBar, NO))
|
|
|
|
{
|
|
|
|
static const NSWindowButton buttons[] = {
|
|
|
|
NSWindowCloseButton,
|
|
|
|
NSWindowMiniaturizeButton,
|
|
|
|
NSWindowZoomButton,
|
|
|
|
NSWindowFullScreenButton,
|
|
|
|
};
|
|
|
|
BOOL hitButton = NO;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
|
|
|
|
{
|
|
|
|
NSButton* button;
|
|
|
|
|
|
|
|
if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
button = [self standardWindowButton:buttons[i]];
|
|
|
|
if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
|
|
|
|
{
|
|
|
|
hitButton = YES;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hitButton)
|
|
|
|
{
|
|
|
|
draggingPhase = 1;
|
|
|
|
dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
|
|
|
|
[[WineApplicationController sharedController] window:self isBeingDragged:YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
|
|
|
|
{
|
|
|
|
if ([self isMovable])
|
|
|
|
{
|
|
|
|
NSPoint point = [self convertBaseToScreen:event.locationInWindow];
|
|
|
|
NSPoint newTopLeft = dragWindowStartPosition;
|
|
|
|
|
|
|
|
newTopLeft.x += point.x - dragStartPosition.x;
|
|
|
|
newTopLeft.y += point.y - dragStartPosition.y;
|
|
|
|
|
|
|
|
if (draggingPhase == 2)
|
|
|
|
{
|
2017-04-24 20:07:10 +02:00
|
|
|
macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
|
|
|
|
mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
|
|
|
|
[queue postEvent:mevent];
|
|
|
|
macdrv_release_event(mevent);
|
2015-03-24 00:58:11 +01:00
|
|
|
|
|
|
|
draggingPhase = 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self setFrameTopLeftPoint:newTopLeft];
|
|
|
|
}
|
|
|
|
else if (draggingPhase == 1 && type == NSLeftMouseDragged)
|
|
|
|
{
|
|
|
|
macdrv_event* event;
|
|
|
|
NSRect frame = [self contentRectForFrameRect:self.frame];
|
|
|
|
|
|
|
|
[[WineApplicationController sharedController] flipRect:&frame];
|
|
|
|
|
|
|
|
event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
|
|
|
|
event->window_restore_requested.keep_frame = TRUE;
|
2016-05-05 20:53:36 +02:00
|
|
|
event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
|
2015-03-24 00:58:11 +01:00
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
|
|
|
|
draggingPhase = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == NSLeftMouseUp)
|
|
|
|
[self endWindowDragging];
|
|
|
|
}
|
|
|
|
|
2013-07-09 09:49:55 +02:00
|
|
|
[super sendEvent:event];
|
2015-03-24 00:58:11 +01:00
|
|
|
}
|
2013-07-09 09:49:55 +02:00
|
|
|
}
|
|
|
|
|
2013-09-30 17:29:23 +02:00
|
|
|
- (void) miniaturize:(id)sender
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
2015-10-23 09:48:34 +02:00
|
|
|
|
|
|
|
WineWindow* parent = (WineWindow*)self.parentWindow;
|
|
|
|
if ([parent isKindOfClass:[WineWindow class]])
|
|
|
|
[parent grabDockIconSnapshotFromWindow:self force:YES];
|
2013-09-30 17:29:23 +02:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
- (void) toggleFullScreen:(id)sender
|
|
|
|
{
|
winemac: Prevent maximized windows from entering Cocoa full-screen mode.
OS X doesn't really have the concept of windows being maximized; that is, being
in a mode where they can't be moved or resized. As a consequence, it doesn't
have a button in the window title bar to restore a maximized window to normal.
So, when a Wine window is maximized, the Mac driver hijacks the green zoom
button to act as a restore button. (When a window is zoomed, the green button
"unzooms" back to its last user size and position, so it's analogous.)
However, with OS X 10.10 (Yosemite), the green button prefers to act as a
toggle for the Cocoa full-screen mode rather than zooming and unzooming. This
made it difficult for users to restore a maximized window. They would have to
Option-click the green button, double-click the title bar, or choose Zoom
from the Window menu, none of which is obvious.
The fix is to disable Cocoa full-screen mode for maximized windows. Then, the
green button reverts to unzoom and restoring the window.
2015-03-13 00:45:25 +01:00
|
|
|
if (!self.disabled && !maximized)
|
2013-10-10 21:22:08 +02:00
|
|
|
[super toggleFullScreen:sender];
|
|
|
|
}
|
|
|
|
|
2015-11-10 03:31:22 +01:00
|
|
|
- (void) setViewsNeedDisplay:(BOOL)value
|
|
|
|
{
|
|
|
|
if (value && ![self viewsNeedDisplay])
|
|
|
|
{
|
|
|
|
WineDisplayLink* link = [self wineDisplayLink];
|
|
|
|
if (link)
|
|
|
|
{
|
|
|
|
NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
|
|
|
|
if (_lastDisplayTime + [link refreshPeriod] < now)
|
|
|
|
[self setAutodisplay:YES];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[link start];
|
|
|
|
_lastDisplayTime = now;
|
|
|
|
}
|
|
|
|
}
|
2017-12-08 09:54:07 +01:00
|
|
|
else
|
|
|
|
[self setAutodisplay:YES];
|
2015-11-10 03:31:22 +01:00
|
|
|
}
|
|
|
|
[super setViewsNeedDisplay:value];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) display
|
|
|
|
{
|
|
|
|
_lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
|
|
|
|
[super display];
|
2017-12-08 09:54:07 +01:00
|
|
|
if (_lastDisplayID)
|
|
|
|
[self setAutodisplay:NO];
|
2015-11-10 03:31:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) displayIfNeeded
|
|
|
|
{
|
|
|
|
_lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
|
|
|
|
[super displayIfNeeded];
|
2017-12-08 09:54:07 +01:00
|
|
|
if (_lastDisplayID)
|
|
|
|
[self setAutodisplay:NO];
|
2015-11-10 03:31:22 +01:00
|
|
|
}
|
|
|
|
|
2018-03-26 03:45:22 +02:00
|
|
|
- (void) setFrame:(NSRect)frameRect display:(BOOL)flag
|
|
|
|
{
|
|
|
|
if (flag)
|
|
|
|
[self setAutodisplay:YES];
|
|
|
|
[super setFrame:frameRect display:flag];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
|
|
|
|
{
|
|
|
|
if (displayFlag)
|
|
|
|
[self setAutodisplay:YES];
|
|
|
|
[super setFrame:frameRect display:displayFlag animate:animateFlag];
|
|
|
|
}
|
|
|
|
|
winemac: Set windows to transparent until they have content to draw, to reduce flicker.
When a window is shown, it may not have drawn its content into the backing
surface, yet. Cocoa will draw the window, starting with its standard light
gray background and then the content view. However, the content view won't
have anything to draw, yet, though, so the window background is not drawn over.
A short while later, usually, the app will paint its content into the window
backing surface and Cocoa will be told to redraw the window. This works, but
the user can often see the flash of the window background color first. This
is especially visible for windows with dark content.
Part of the fix is to set the window background to transparent until the
content view has actually drawn once since the window was shown.
That's not sufficient on its own, though. We had disabled Cocoa's automatic
display mechanism for windows and put display on a display-link timer. This
meant that the window was not actually cleared to its transparent color. When
the window was shown, the Window Server displayed a white backing buffer. It
is the app process which should fill that backing buffer with clear color but,
because we had disabled auto-display, that wasn't getting done at the same
time the window was displayed. It was happening some time after. Again, the
result was a visible flicker of white.
So, we now temporarily re-enable auto-display just before showing a window.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2016-09-02 10:08:01 +02:00
|
|
|
- (void) windowDidDrawContent
|
|
|
|
{
|
|
|
|
if (!drawnSinceShown)
|
|
|
|
{
|
|
|
|
drawnSinceShown = YES;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
[self checkTransparency];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-17 15:59:09 +01:00
|
|
|
- (NSArray*) childWineWindows
|
|
|
|
{
|
|
|
|
NSArray* childWindows = self.childWindows;
|
|
|
|
NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
|
|
|
|
return [child isKindOfClass:[WineWindow class]];
|
|
|
|
}];
|
|
|
|
return [childWindows objectsAtIndexes:indexes];
|
|
|
|
}
|
|
|
|
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
- (void) updateForGLSubviews
|
|
|
|
{
|
|
|
|
if (gl_surface_mode == GL_SURFACE_BEHIND)
|
|
|
|
[self checkTransparency];
|
|
|
|
}
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
- (void) setRetinaMode:(int)mode
|
|
|
|
{
|
|
|
|
NSRect frame;
|
|
|
|
double scale = mode ? 0.5 : 2.0;
|
|
|
|
NSAffineTransform* transform = [NSAffineTransform transform];
|
|
|
|
|
|
|
|
[transform scaleBy:scale];
|
|
|
|
|
|
|
|
if (shape)
|
|
|
|
[shape transformUsingAffineTransform:transform];
|
|
|
|
|
2018-07-13 16:27:00 +02:00
|
|
|
for (WineBaseView* subview in [self.contentView subviews])
|
2016-05-05 20:53:36 +02:00
|
|
|
{
|
2018-07-13 16:27:00 +02:00
|
|
|
if ([subview isKindOfClass:[WineBaseView class]])
|
2016-05-19 01:38:25 +02:00
|
|
|
[subview setRetinaMode:mode];
|
2016-05-05 20:53:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
|
|
|
|
frame.origin.x *= scale;
|
|
|
|
frame.origin.y *= scale;
|
|
|
|
frame.size.width *= scale;
|
|
|
|
frame.size.height *= scale;
|
|
|
|
frame = [self frameRectForContentRect:frame];
|
|
|
|
|
|
|
|
savedContentMinSize = [transform transformSize:savedContentMinSize];
|
|
|
|
if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
|
|
|
|
savedContentMaxSize.width *= scale;
|
|
|
|
if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
|
|
|
|
savedContentMaxSize.height *= scale;
|
|
|
|
|
|
|
|
self.contentMinSize = [transform transformSize:self.contentMinSize];
|
|
|
|
NSSize temp = self.contentMaxSize;
|
|
|
|
if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
|
|
|
|
temp.width *= scale;
|
|
|
|
if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
|
|
|
|
temp.height *= scale;
|
|
|
|
self.contentMaxSize = temp;
|
|
|
|
|
|
|
|
ignore_windowResize = TRUE;
|
|
|
|
[self setFrameAndWineFrame:frame];
|
|
|
|
ignore_windowResize = FALSE;
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-01-27 23:19:37 +01:00
|
|
|
/*
|
|
|
|
* ---------- NSResponder method overrides ----------
|
|
|
|
*/
|
2017-03-10 09:22:02 +01:00
|
|
|
- (void) keyDown:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
if ([theEvent isARepeat])
|
|
|
|
{
|
|
|
|
if (!allowKeyRepeats)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
allowKeyRepeats = YES;
|
|
|
|
|
|
|
|
[self postKeyEvent:theEvent];
|
|
|
|
}
|
2013-02-04 00:20:18 +01:00
|
|
|
|
|
|
|
- (void) flagsChanged:(NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
static const struct {
|
|
|
|
NSUInteger mask;
|
|
|
|
uint16_t keycode;
|
|
|
|
} modifiers[] = {
|
|
|
|
{ NX_ALPHASHIFTMASK, kVK_CapsLock },
|
|
|
|
{ NX_DEVICELSHIFTKEYMASK, kVK_Shift },
|
|
|
|
{ NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
|
|
|
|
{ NX_DEVICELCTLKEYMASK, kVK_Control },
|
|
|
|
{ NX_DEVICERCTLKEYMASK, kVK_RightControl },
|
|
|
|
{ NX_DEVICELALTKEYMASK, kVK_Option },
|
|
|
|
{ NX_DEVICERALTKEYMASK, kVK_RightOption },
|
|
|
|
{ NX_DEVICELCMDKEYMASK, kVK_Command },
|
|
|
|
{ NX_DEVICERCMDKEYMASK, kVK_RightCommand },
|
|
|
|
};
|
|
|
|
|
2018-09-12 11:35:30 +02:00
|
|
|
NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
|
2013-02-04 00:20:18 +01:00
|
|
|
NSUInteger changed;
|
|
|
|
int i, last_changed;
|
|
|
|
|
|
|
|
fix_device_modifiers_by_generic(&modifierFlags);
|
|
|
|
changed = modifierFlags ^ lastModifierFlags;
|
|
|
|
|
|
|
|
last_changed = -1;
|
|
|
|
for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
|
|
|
|
if (changed & modifiers[i].mask)
|
|
|
|
last_changed = i;
|
|
|
|
|
|
|
|
for (i = 0; i <= last_changed; i++)
|
|
|
|
{
|
|
|
|
if (changed & modifiers[i].mask)
|
|
|
|
{
|
|
|
|
BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
|
|
|
|
|
2017-03-10 09:22:02 +01:00
|
|
|
if (pressed)
|
|
|
|
allowKeyRepeats = NO;
|
|
|
|
|
2013-02-04 00:20:18 +01:00
|
|
|
if (i == last_changed)
|
|
|
|
lastModifierFlags = modifierFlags;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lastModifierFlags ^= modifiers[i].mask;
|
|
|
|
fix_generic_modifiers_by_device(&lastModifierFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Caps lock generates one event for each press-release action.
|
|
|
|
// We need to simulate a pair of events for each actual event.
|
|
|
|
if (modifiers[i].mask == NX_ALPHASHIFTMASK)
|
|
|
|
{
|
|
|
|
[self postKey:modifiers[i].keycode
|
|
|
|
pressed:TRUE
|
|
|
|
modifiers:lastModifierFlags
|
|
|
|
event:(NSEvent*)theEvent];
|
|
|
|
pressed = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self postKey:modifiers[i].keycode
|
|
|
|
pressed:pressed
|
|
|
|
modifiers:lastModifierFlags
|
|
|
|
event:(NSEvent*)theEvent];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-31 08:05:09 +01:00
|
|
|
- (void) applicationWillHide
|
|
|
|
{
|
|
|
|
savedVisibleState = [self isVisible];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) applicationDidUnhide
|
|
|
|
{
|
|
|
|
if ([self isVisible])
|
|
|
|
[self becameEligibleParentOrChild];
|
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:37 +01:00
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
/*
|
|
|
|
* ---------- NSWindowDelegate methods ----------
|
|
|
|
*/
|
2013-10-10 21:22:08 +02:00
|
|
|
- (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;
|
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:48 +01:00
|
|
|
- (void)windowDidBecomeKey:(NSNotification *)notification
|
|
|
|
{
|
2013-04-16 07:38:01 +02:00
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
|
|
|
NSEvent* event = [controller lastFlagsChanged];
|
2013-02-04 00:20:18 +01:00
|
|
|
if (event)
|
|
|
|
[self flagsChanged:event];
|
|
|
|
|
2013-09-25 22:10:57 +02:00
|
|
|
if (causing_becomeKeyWindow == self) return;
|
2013-01-27 23:19:48 +01:00
|
|
|
|
2013-04-16 07:38:01 +02:00
|
|
|
[controller windowGotFocus:self];
|
2013-01-27 23:19:48 +01:00
|
|
|
}
|
|
|
|
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
- (void) windowDidChangeOcclusionState:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
[self checkWineDisplayLink];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowDidChangeScreen:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
[self checkWineDisplayLink];
|
|
|
|
}
|
|
|
|
|
2013-02-04 00:20:07 +01:00
|
|
|
- (void)windowDidDeminiaturize:(NSNotification *)notification
|
|
|
|
{
|
2013-06-04 11:59:51 +02:00
|
|
|
WineApplicationController* controller = [WineApplicationController sharedController];
|
|
|
|
|
2013-02-04 00:20:07 +01:00
|
|
|
if (!ignore_windowDeminiaturize)
|
2013-10-10 21:22:08 +02:00
|
|
|
[self postDidUnminimizeEvent];
|
2013-02-04 00:20:07 +01:00
|
|
|
ignore_windowDeminiaturize = FALSE;
|
2013-02-18 02:28:27 +01:00
|
|
|
|
2013-08-30 07:00:58 +02:00
|
|
|
[self becameEligibleParentOrChild];
|
|
|
|
|
2013-06-07 00:43:06 +02:00
|
|
|
if (fullscreen && [self isOnActiveSpace])
|
|
|
|
[controller updateFullscreenWindows];
|
2013-06-04 11:59:51 +02:00
|
|
|
[controller adjustWindowLevels];
|
|
|
|
|
2013-10-08 09:21:34 +02:00
|
|
|
if (![self parentWindow])
|
|
|
|
[self postBroughtForwardEvent];
|
|
|
|
|
2013-06-04 11:59:51 +02:00
|
|
|
if (!self.disabled && !self.noActivate)
|
|
|
|
{
|
2013-09-25 22:10:57 +02:00
|
|
|
causing_becomeKeyWindow = self;
|
2013-06-04 11:59:51 +02:00
|
|
|
[self makeKeyWindow];
|
2013-09-25 22:10:57 +02:00
|
|
|
causing_becomeKeyWindow = nil;
|
2013-06-04 11:59:51 +02:00
|
|
|
[controller windowGotFocus:self];
|
|
|
|
}
|
2013-06-04 11:59:54 +02:00
|
|
|
|
|
|
|
[self windowDidResize:notification];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[self checkWineDisplayLink];
|
2013-02-04 00:20:07 +01:00
|
|
|
}
|
|
|
|
|
2013-04-02 10:17:48 +02:00
|
|
|
- (void) windowDidEndLiveResize:(NSNotification *)notification
|
|
|
|
{
|
2015-03-24 00:58:06 +01:00
|
|
|
if (!maximized)
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
2013-04-02 10:17:48 +02:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
- (void) windowDidEnterFullScreen:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
enteringFullScreen = FALSE;
|
|
|
|
enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
|
2017-04-24 20:07:13 +02:00
|
|
|
if (pendingOrderOut)
|
|
|
|
[self doOrderOut];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowDidExitFullScreen:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
exitingFullScreen = FALSE;
|
2016-05-05 20:53:36 +02:00
|
|
|
[self setFrameAndWineFrame:nonFullscreenFrame];
|
2013-10-10 21:22:08 +02:00
|
|
|
[self windowDidResize:nil];
|
2017-04-24 20:07:13 +02:00
|
|
|
if (pendingOrderOut)
|
|
|
|
[self doOrderOut];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowDidFailToEnterFullScreen:(NSWindow*)window
|
|
|
|
{
|
|
|
|
enteringFullScreen = FALSE;
|
|
|
|
enteredFullScreenTime = 0;
|
2017-04-24 20:07:13 +02:00
|
|
|
if (pendingOrderOut)
|
|
|
|
[self doOrderOut];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowDidFailToExitFullScreen:(NSWindow*)window
|
|
|
|
{
|
|
|
|
exitingFullScreen = FALSE;
|
|
|
|
[self windowDidResize:nil];
|
2017-04-24 20:07:13 +02:00
|
|
|
if (pendingOrderOut)
|
|
|
|
[self doOrderOut];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
2013-06-07 00:43:06 +02:00
|
|
|
- (void)windowDidMiniaturize:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
if (fullscreen && [self isOnActiveSpace])
|
|
|
|
[[WineApplicationController sharedController] updateFullscreenWindows];
|
winemac: Use CVDisplayLink to limit window redrawing to the display refresh rate.
Some Windows apps cause user32 to flush the window surface much faster than the
display refresh rate. The Mac driver only marks its window as needing to be
redrawn and lets Cocoa decide how often to actually redraw. Unfortunately,
Cocoa redraws each time through the run loop and, since the Mac driver uses a
run loop source to convey messages from background threads to the main thread,
it redraws after every batch of messages.
On some versions of OS X, this excessive drawing provokes synchronization with
the window server's buffer swaps, preventing the main thread from being
responsive. Even when that doesn't happen, it's wasteful.
So, we set our windows' autodisplay property to false so that Cocoa never
displays windows itself. Then, we arrange to call -displayIfNeeded once per
display refresh cycle using a CVDisplayLink. We maintain one CVDisplayLink per
display (on demand), move windows among them as the windows change screens,
start them when they acquire their first window, and stop them when they have
none left.
Signed-off-by: Ken Thomases <ken@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2015-11-06 13:57:53 +01:00
|
|
|
[self checkWineDisplayLink];
|
2013-06-07 00:43:06 +02:00
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:29 +01:00
|
|
|
- (void)windowDidMove:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
[self windowDidResize:notification];
|
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:53 +01:00
|
|
|
- (void)windowDidResignKey:(NSNotification *)notification
|
|
|
|
{
|
2013-04-04 01:56:35 +02:00
|
|
|
macdrv_event* event;
|
2013-01-27 23:19:53 +01:00
|
|
|
|
|
|
|
if (causing_becomeKeyWindow) return;
|
|
|
|
|
2013-04-04 01:56:35 +02:00
|
|
|
event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
2013-01-27 23:19:53 +01:00
|
|
|
}
|
|
|
|
|
2013-01-27 23:19:29 +01:00
|
|
|
- (void)windowDidResize:(NSNotification *)notification
|
|
|
|
{
|
2016-05-05 20:53:36 +02:00
|
|
|
NSRect frame = self.wine_fractionalFrame;
|
2014-04-24 03:00:29 +02:00
|
|
|
|
|
|
|
if ([self inLiveResize])
|
|
|
|
{
|
|
|
|
if (NSMinX(frame) != NSMinX(frameAtResizeStart))
|
|
|
|
resizingFromLeft = TRUE;
|
|
|
|
if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
|
|
|
|
resizingFromTop = TRUE;
|
|
|
|
}
|
|
|
|
|
2013-11-15 03:52:07 +01:00
|
|
|
if (ignore_windowResize || exitingFullScreen) return;
|
2013-10-10 21:22:08 +02:00
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
if ([self preventResizing])
|
2013-05-14 10:35:03 +02:00
|
|
|
{
|
2016-10-11 05:38:13 +02:00
|
|
|
NSRect contentRect = [self contentRectForFrameRect:frame];
|
|
|
|
[self setContentMinSize:contentRect.size];
|
|
|
|
[self setContentMaxSize:contentRect.size];
|
2013-05-14 10:35:03 +02:00
|
|
|
}
|
2013-01-27 23:19:29 +01:00
|
|
|
|
2016-10-11 05:38:13 +02:00
|
|
|
[self postWindowFrameChanged:frame
|
|
|
|
fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
|
|
|
|
resizing:[self inLiveResize]];
|
2013-05-16 23:48:39 +02:00
|
|
|
|
|
|
|
[[[self contentView] inputContext] invalidateCharacterCoordinates];
|
2013-06-07 00:43:06 +02:00
|
|
|
[self updateFullscreen];
|
2013-01-27 23:19:29 +01:00
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
- (BOOL)windowShouldClose:(id)sender
|
|
|
|
{
|
2013-04-04 01:56:35 +02:00
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
2013-01-07 21:44:44 +01:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2013-12-30 04:34:48 +01:00
|
|
|
- (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
|
|
|
|
{
|
|
|
|
if (maximized)
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
else if (!resizable)
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2013-08-30 07:00:51 +02:00
|
|
|
- (void) windowWillClose:(NSNotification*)notification
|
|
|
|
{
|
2013-08-30 07:00:56 +02:00
|
|
|
WineWindow* child;
|
|
|
|
|
2013-09-27 06:46:31 +02:00
|
|
|
if (fakingClose) return;
|
2013-08-30 07:00:56 +02:00
|
|
|
if (latentParentWindow)
|
|
|
|
{
|
|
|
|
[latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
|
|
|
|
self.latentParentWindow = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (child in latentChildWindows)
|
|
|
|
{
|
|
|
|
if (child.latentParentWindow == self)
|
|
|
|
child.latentParentWindow = nil;
|
|
|
|
}
|
|
|
|
[latentChildWindows removeAllObjects];
|
2013-08-30 07:00:51 +02:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:22:08 +02:00
|
|
|
- (void) windowWillEnterFullScreen:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
enteringFullScreen = TRUE;
|
2016-05-05 20:53:36 +02:00
|
|
|
nonFullscreenFrame = self.wine_fractionalFrame;
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowWillExitFullScreen:(NSNotification*)notification
|
|
|
|
{
|
|
|
|
exitingFullScreen = TRUE;
|
2016-10-11 05:38:13 +02:00
|
|
|
[self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
|
2013-10-10 21:22:08 +02:00
|
|
|
}
|
|
|
|
|
2013-02-04 00:20:07 +01:00
|
|
|
- (void)windowWillMiniaturize:(NSNotification *)notification
|
|
|
|
{
|
2013-08-30 07:00:58 +02:00
|
|
|
[self becameIneligibleParentOrChild];
|
2015-10-23 09:48:34 +02:00
|
|
|
[self grabDockIconSnapshotFromWindow:nil force:NO];
|
2013-02-04 00:20:07 +01:00
|
|
|
}
|
|
|
|
|
2014-04-24 03:00:29 +02:00
|
|
|
- (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
|
|
|
|
{
|
|
|
|
if ([self inLiveResize])
|
|
|
|
{
|
2015-03-24 00:58:06 +01:00
|
|
|
if (maximized)
|
2016-05-05 20:53:36 +02:00
|
|
|
return self.wine_fractionalFrame.size;
|
2015-03-24 00:58:06 +01:00
|
|
|
|
2014-04-24 03:00:29 +02:00
|
|
|
NSRect rect;
|
|
|
|
macdrv_query* query;
|
|
|
|
|
|
|
|
rect = [self frame];
|
|
|
|
if (resizingFromLeft)
|
|
|
|
rect.origin.x = NSMaxX(rect) - frameSize.width;
|
|
|
|
if (!resizingFromTop)
|
|
|
|
rect.origin.y = NSMaxY(rect) - frameSize.height;
|
|
|
|
rect.size = frameSize;
|
|
|
|
rect = [self contentRectForFrameRect:rect];
|
|
|
|
[[WineApplicationController sharedController] flipRect:&rect];
|
|
|
|
|
|
|
|
query = macdrv_create_query();
|
|
|
|
query->type = QUERY_RESIZE_SIZE;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
2016-05-05 20:53:36 +02:00
|
|
|
query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
|
2014-04-24 03:00:29 +02:00
|
|
|
query->resize_size.from_left = resizingFromLeft;
|
|
|
|
query->resize_size.from_top = resizingFromTop;
|
|
|
|
|
|
|
|
if ([self.queue query:query timeout:0.1])
|
|
|
|
{
|
2016-05-05 20:53:36 +02:00
|
|
|
rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
|
2014-04-24 03:00:29 +02:00
|
|
|
rect = [self frameRectForContentRect:rect];
|
|
|
|
frameSize = rect.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
macdrv_release_query(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
return frameSize;
|
|
|
|
}
|
|
|
|
|
2013-04-02 10:17:48 +02:00
|
|
|
- (void) windowWillStartLiveResize:(NSNotification *)notification
|
|
|
|
{
|
2015-03-24 00:58:11 +01:00
|
|
|
[self endWindowDragging];
|
|
|
|
|
2015-03-24 00:58:06 +01:00
|
|
|
if (maximized)
|
|
|
|
{
|
|
|
|
macdrv_event* event;
|
|
|
|
NSRect frame = [self contentRectForFrameRect:self.frame];
|
2013-09-18 20:00:37 +02:00
|
|
|
|
2015-03-24 00:58:06 +01:00
|
|
|
[[WineApplicationController sharedController] flipRect:&frame];
|
|
|
|
|
|
|
|
event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
|
|
|
|
event->window_restore_requested.keep_frame = TRUE;
|
2016-05-05 20:53:36 +02:00
|
|
|
event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
|
2015-03-24 00:58:06 +01:00
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
[self sendResizeStartQuery];
|
2013-09-18 20:00:37 +02:00
|
|
|
|
2014-04-24 03:00:29 +02:00
|
|
|
frameAtResizeStart = [self frame];
|
|
|
|
resizingFromLeft = resizingFromTop = FALSE;
|
2013-04-02 10:17:48 +02:00
|
|
|
}
|
|
|
|
|
2013-10-08 09:21:29 +02:00
|
|
|
- (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
|
|
|
|
{
|
|
|
|
macdrv_query* query;
|
|
|
|
NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
|
|
|
|
NSSize maxSize;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
currentContentRect = [self contentRectForFrameRect:[self frame]];
|
|
|
|
proposedContentRect = [self contentRectForFrameRect:proposedFrame];
|
|
|
|
|
|
|
|
maxSize = [self contentMaxSize];
|
|
|
|
newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
|
|
|
|
newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
|
|
|
|
|
|
|
|
// Try to keep the top-left corner where it is.
|
|
|
|
newContentRect.origin.x = NSMinX(currentContentRect);
|
|
|
|
newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
|
|
|
|
|
|
|
|
// If that pushes the bottom or right off the screen, pull it up and to the left.
|
|
|
|
screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
|
|
|
|
if (NSMaxX(newContentRect) > NSMaxX(screenRect))
|
|
|
|
newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
|
|
|
|
if (NSMinY(newContentRect) < NSMinY(screenRect))
|
|
|
|
newContentRect.origin.y = NSMinY(screenRect);
|
|
|
|
|
|
|
|
// If that pushes the top or left off the screen, push it down and the right
|
|
|
|
// again. Do this last because the top-left corner is more important than the
|
|
|
|
// bottom-right.
|
|
|
|
if (NSMinX(newContentRect) < NSMinX(screenRect))
|
|
|
|
newContentRect.origin.x = NSMinX(screenRect);
|
|
|
|
if (NSMaxY(newContentRect) > NSMaxY(screenRect))
|
|
|
|
newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
|
|
|
|
|
|
|
|
return [self frameRectForContentRect:newContentRect];
|
|
|
|
}
|
|
|
|
|
2013-03-11 04:58:51 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ---------- NSPasteboardOwner methods ----------
|
|
|
|
*/
|
|
|
|
- (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
|
|
|
|
{
|
|
|
|
macdrv_query* query = macdrv_create_query();
|
|
|
|
query->type = QUERY_PASTEBOARD_DATA;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
|
|
|
query->pasteboard_data.type = (CFStringRef)[type copy];
|
|
|
|
|
|
|
|
[self.queue query:query timeout:3];
|
|
|
|
macdrv_release_query(query);
|
|
|
|
}
|
|
|
|
|
2016-10-23 20:03:34 +02:00
|
|
|
- (void) pasteboardChangedOwner:(NSPasteboard*)sender
|
|
|
|
{
|
|
|
|
macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
|
|
|
|
[queue postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
}
|
|
|
|
|
2013-03-13 22:53:32 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ---------- NSDraggingDestination methods ----------
|
|
|
|
*/
|
|
|
|
- (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
|
|
|
return [self draggingUpdated:sender];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) draggingExited:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
|
|
|
// This isn't really a query. We don't need any response. However, it
|
|
|
|
// has to be processed in a similar manner as the other drag-and-drop
|
|
|
|
// queries in order to maintain the proper order of operations.
|
|
|
|
macdrv_query* query = macdrv_create_query();
|
|
|
|
query->type = QUERY_DRAG_EXITED;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
|
|
|
|
|
|
|
[self.queue query:query timeout:0.1];
|
|
|
|
macdrv_release_query(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
|
|
|
NSDragOperation ret;
|
|
|
|
NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
|
2016-05-05 20:53:36 +02:00
|
|
|
CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
|
2013-03-13 22:53:32 +01:00
|
|
|
NSPasteboard* pb = [sender draggingPasteboard];
|
|
|
|
|
|
|
|
macdrv_query* query = macdrv_create_query();
|
|
|
|
query->type = QUERY_DRAG_OPERATION;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
2016-05-05 20:53:36 +02:00
|
|
|
query->drag_operation.x = floor(cgpt.x);
|
|
|
|
query->drag_operation.y = floor(cgpt.y);
|
2013-03-13 22:53:32 +01:00
|
|
|
query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
|
|
|
|
query->drag_operation.accepted_op = NSDragOperationNone;
|
|
|
|
query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
|
|
|
|
|
|
|
|
[self.queue query:query timeout:3];
|
|
|
|
ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
|
|
|
|
macdrv_release_query(query);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
|
|
|
|
{
|
|
|
|
BOOL ret;
|
|
|
|
NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
|
2016-05-05 20:53:36 +02:00
|
|
|
CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
|
2013-03-13 22:53:32 +01:00
|
|
|
NSPasteboard* pb = [sender draggingPasteboard];
|
|
|
|
|
|
|
|
macdrv_query* query = macdrv_create_query();
|
|
|
|
query->type = QUERY_DRAG_DROP;
|
|
|
|
query->window = (macdrv_window)[self retain];
|
2016-05-05 20:53:36 +02:00
|
|
|
query->drag_drop.x = floor(cgpt.x);
|
|
|
|
query->drag_drop.y = floor(cgpt.y);
|
2013-03-13 22:53:32 +01:00
|
|
|
query->drag_drop.op = [sender draggingSourceOperationMask];
|
|
|
|
query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
|
|
|
|
|
2016-02-05 00:18:26 +01:00
|
|
|
[self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
|
2013-03-13 22:53:32 +01:00
|
|
|
ret = query->status;
|
|
|
|
macdrv_release_query(query);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) wantsPeriodicDraggingUpdates
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_create_cocoa_window
|
|
|
|
*
|
|
|
|
* Create a Cocoa window with the given content frame and features (e.g.
|
|
|
|
* title bar, close box, etc.).
|
|
|
|
*/
|
|
|
|
macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
|
2013-01-21 07:08:03 +01:00
|
|
|
CGRect frame, void* hwnd, macdrv_event_queue queue)
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
|
|
|
__block WineWindow* window;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
window = [[WineWindow createWindowWithFeatures:wf
|
2016-05-05 20:53:36 +02:00
|
|
|
windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
|
2013-01-21 07:08:03 +01:00
|
|
|
hwnd:hwnd
|
2013-01-21 07:07:58 +01:00
|
|
|
queue:(WineEventQueue*)queue] retain];
|
2013-01-07 21:44:44 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return (macdrv_window)window;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_destroy_cocoa_window
|
|
|
|
*
|
|
|
|
* Destroy a Cocoa window.
|
|
|
|
*/
|
|
|
|
void macdrv_destroy_cocoa_window(macdrv_window w)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
2013-10-09 23:30:53 +02:00
|
|
|
OnMainThread(^{
|
|
|
|
[window doOrderOut];
|
|
|
|
[window close];
|
|
|
|
});
|
2013-01-21 07:07:58 +01:00
|
|
|
[window.queue discardEventsMatchingMask:-1 forWindow:window];
|
2013-01-07 21:44:44 +01:00
|
|
|
[window release];
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
2013-01-21 07:08:03 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_get_window_hwnd
|
|
|
|
*
|
|
|
|
* Get the hwnd that was set for the window at creation.
|
|
|
|
*/
|
|
|
|
void* macdrv_get_window_hwnd(macdrv_window w)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
return window.hwnd;
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_cocoa_window_features
|
|
|
|
*
|
|
|
|
* Update a Cocoa window's features.
|
|
|
|
*/
|
|
|
|
void macdrv_set_cocoa_window_features(macdrv_window w,
|
|
|
|
const struct macdrv_window_features* wf)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
[window setWindowFeatures:wf];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:18:05 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_cocoa_window_state
|
|
|
|
*
|
|
|
|
* Update a Cocoa window's state.
|
|
|
|
*/
|
|
|
|
void macdrv_set_cocoa_window_state(macdrv_window w,
|
|
|
|
const struct macdrv_window_state* state)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
[window setMacDrvState:state];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:44:44 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_cocoa_window_title
|
|
|
|
*
|
|
|
|
* Set a Cocoa window's title.
|
|
|
|
*/
|
|
|
|
void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
|
|
|
|
size_t length)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
NSString* titleString;
|
|
|
|
|
|
|
|
if (title)
|
|
|
|
titleString = [NSString stringWithCharacters:title length:length];
|
|
|
|
else
|
|
|
|
titleString = @"";
|
|
|
|
OnMainThreadAsync(^{
|
|
|
|
[window setTitle:titleString];
|
2013-05-17 01:43:25 +02:00
|
|
|
if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
|
2013-01-11 13:21:06 +01:00
|
|
|
[NSApp changeWindowsItem:window title:titleString filename:NO];
|
2013-01-07 21:44:44 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_order_cocoa_window
|
|
|
|
*
|
|
|
|
* Reorder a Cocoa window relative to other windows. If prev is
|
|
|
|
* non-NULL, it is ordered below that window. Else, if next is non-NULL,
|
|
|
|
* it is ordered above that window. Otherwise, it is ordered to the
|
|
|
|
* front.
|
|
|
|
*/
|
2013-11-15 03:52:04 +01:00
|
|
|
void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
|
|
|
|
macdrv_window n, int activate)
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
2013-11-15 03:52:04 +01:00
|
|
|
WineWindow* prev = (WineWindow*)p;
|
|
|
|
WineWindow* next = (WineWindow*)n;
|
2013-01-07 21:44:44 +01:00
|
|
|
|
2013-11-15 03:52:04 +01:00
|
|
|
OnMainThreadAsync(^{
|
|
|
|
[window orderBelow:prev
|
|
|
|
orAbove:next
|
|
|
|
activate:activate];
|
2013-01-07 21:44:44 +01:00
|
|
|
});
|
2013-12-31 08:05:18 +01:00
|
|
|
[window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
|
|
|
|
forWindow:window];
|
|
|
|
[next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
|
|
|
|
forWindow:next];
|
2013-01-07 21:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_hide_cocoa_window
|
|
|
|
*
|
|
|
|
* Hides a Cocoa window.
|
|
|
|
*/
|
|
|
|
void macdrv_hide_cocoa_window(macdrv_window w)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
2013-01-11 13:19:36 +01:00
|
|
|
[window doOrderOut];
|
2013-01-07 21:44:44 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_cocoa_window_frame
|
|
|
|
*
|
2013-11-15 03:52:04 +01:00
|
|
|
* Move a Cocoa window.
|
2013-01-07 21:44:44 +01:00
|
|
|
*/
|
2013-11-15 03:52:04 +01:00
|
|
|
void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
|
2013-01-07 21:44:44 +01:00
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
2013-11-20 21:01:29 +01:00
|
|
|
OnMainThread(^{
|
2016-05-05 20:53:36 +02:00
|
|
|
[window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
|
2013-01-07 21:44:44 +01:00
|
|
|
});
|
|
|
|
}
|
2013-01-11 13:19:36 +01:00
|
|
|
|
2013-01-27 23:19:29 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_get_cocoa_window_frame
|
|
|
|
*
|
|
|
|
* Gets the frame of a Cocoa window.
|
|
|
|
*/
|
|
|
|
void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
NSRect frame;
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
|
2013-04-16 07:38:01 +02:00
|
|
|
[[WineApplicationController sharedController] flipRect:&frame];
|
2016-05-05 20:53:36 +02:00
|
|
|
*out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
|
2013-01-27 23:19:29 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-01-11 13:19:36 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_cocoa_parent_window
|
|
|
|
*
|
|
|
|
* Sets the parent window for a Cocoa window. If parent is NULL, clears
|
|
|
|
* the parent window.
|
|
|
|
*/
|
|
|
|
void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
[window setMacDrvParentWindow:(WineWindow*)parent];
|
|
|
|
});
|
|
|
|
}
|
2013-01-15 03:23:55 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_window_surface
|
|
|
|
*/
|
|
|
|
void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
window.surface = surface;
|
|
|
|
window.surface_mutex = mutex;
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_window_needs_display
|
|
|
|
*
|
|
|
|
* Mark a window as needing display in a specified rect (in non-client
|
|
|
|
* area coordinates).
|
|
|
|
*/
|
|
|
|
void macdrv_window_needs_display(macdrv_window w, CGRect rect)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThreadAsync(^{
|
2016-05-05 20:53:36 +02:00
|
|
|
[[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
|
2013-01-15 03:23:55 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
2013-01-15 03:23:58 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_window_shape
|
|
|
|
*
|
|
|
|
* Sets the shape of a Cocoa window from an array of rectangles. If
|
|
|
|
* rects is NULL, resets the window's shape to its frame.
|
|
|
|
*/
|
|
|
|
void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
if (!rects || !count)
|
2014-10-03 00:06:19 +02:00
|
|
|
{
|
2013-01-15 03:23:58 +01:00
|
|
|
window.shape = nil;
|
2014-10-03 00:06:19 +02:00
|
|
|
window.shapeData = nil;
|
2015-10-23 09:48:34 +02:00
|
|
|
[window checkEmptyShaped];
|
2014-10-03 00:06:19 +02:00
|
|
|
}
|
2013-01-15 03:23:58 +01:00
|
|
|
else
|
|
|
|
{
|
2014-10-03 00:06:19 +02:00
|
|
|
size_t length = sizeof(*rects) * count;
|
|
|
|
if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
|
|
|
|
{
|
|
|
|
NSBezierPath* path;
|
|
|
|
unsigned int i;
|
2013-01-15 03:23:58 +01:00
|
|
|
|
2014-10-03 00:06:19 +02:00
|
|
|
path = [NSBezierPath bezierPath];
|
|
|
|
for (i = 0; i < count; i++)
|
2016-05-05 20:53:36 +02:00
|
|
|
[path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
|
2014-10-03 00:06:19 +02:00
|
|
|
window.shape = path;
|
|
|
|
window.shapeData = [NSData dataWithBytes:rects length:length];
|
2015-10-23 09:48:34 +02:00
|
|
|
[window checkEmptyShaped];
|
2014-10-03 00:06:19 +02:00
|
|
|
}
|
2013-01-15 03:23:58 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
2013-01-15 03:24:02 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_window_alpha
|
|
|
|
*/
|
|
|
|
void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
[window setAlphaValue:alpha];
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_window_color_key
|
|
|
|
*/
|
|
|
|
void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
|
|
|
|
CGFloat keyBlue)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
window.colorKeyed = TRUE;
|
|
|
|
window.colorKeyRed = keyRed;
|
|
|
|
window.colorKeyGreen = keyGreen;
|
|
|
|
window.colorKeyBlue = keyBlue;
|
|
|
|
[window checkTransparency];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_clear_window_color_key
|
|
|
|
*/
|
|
|
|
void macdrv_clear_window_color_key(macdrv_window w)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
window.colorKeyed = FALSE;
|
|
|
|
[window checkTransparency];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_window_use_per_pixel_alpha
|
|
|
|
*/
|
|
|
|
void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
window.usePerPixelAlpha = use_per_pixel_alpha;
|
|
|
|
[window checkTransparency];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
2013-01-27 23:19:41 +01:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_give_cocoa_window_focus
|
|
|
|
*
|
|
|
|
* Makes the Cocoa window "key" (gives it keyboard focus). This also
|
|
|
|
* orders it front and, if its frame was not within the desktop bounds,
|
|
|
|
* Cocoa will typically move it on-screen.
|
|
|
|
*/
|
2013-04-24 23:10:10 +02:00
|
|
|
void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
|
2013-01-27 23:19:41 +01:00
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
2013-04-24 23:10:10 +02:00
|
|
|
[window makeFocused:activate];
|
2013-01-27 23:19:41 +01:00
|
|
|
});
|
|
|
|
}
|
2013-03-06 11:59:07 +01:00
|
|
|
|
2013-09-18 20:00:37 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_window_min_max_sizes
|
|
|
|
*
|
|
|
|
* Sets the window's minimum and maximum content sizes.
|
|
|
|
*/
|
|
|
|
void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
|
|
|
|
{
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
2016-05-05 20:53:36 +02:00
|
|
|
[window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
|
2013-09-18 20:00:37 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_create_view
|
|
|
|
*
|
2016-05-13 01:50:43 +02:00
|
|
|
* Creates and returns a view with the specified frame rect. The
|
2013-03-06 11:59:07 +01:00
|
|
|
* caller is responsible for calling macdrv_dispose_view() on the view
|
|
|
|
* when it is done with it.
|
|
|
|
*/
|
2016-05-13 01:50:43 +02:00
|
|
|
macdrv_view macdrv_create_view(CGRect rect)
|
2013-03-06 11:59:07 +01:00
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
__block WineContentView* view;
|
|
|
|
|
|
|
|
if (CGRectIsNull(rect)) rect = CGRectZero;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
|
2013-03-06 11:59:07 +01:00
|
|
|
[view setAutoresizesSubviews:NO];
|
2018-07-13 16:27:44 +02:00
|
|
|
[view setAutoresizingMask:NSViewNotSizable];
|
2016-05-13 01:50:42 +02:00
|
|
|
[view setHidden:YES];
|
2013-03-06 11:59:07 +01:00
|
|
|
[nc addObserver:view
|
|
|
|
selector:@selector(updateGLContexts)
|
|
|
|
name:NSViewGlobalFrameDidChangeNotification
|
|
|
|
object:view];
|
|
|
|
[nc addObserver:view
|
|
|
|
selector:@selector(updateGLContexts)
|
|
|
|
name:NSApplicationDidChangeScreenParametersNotification
|
|
|
|
object:NSApp];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
return (macdrv_view)view;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_dispose_view
|
|
|
|
*
|
|
|
|
* Destroys a view previously returned by macdrv_create_view.
|
|
|
|
*/
|
|
|
|
void macdrv_dispose_view(macdrv_view v)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
|
2013-07-02 08:24:19 +02:00
|
|
|
WineWindow* window = (WineWindow*)[view window];
|
2013-03-06 11:59:07 +01:00
|
|
|
|
|
|
|
[nc removeObserver:view
|
|
|
|
name:NSViewGlobalFrameDidChangeNotification
|
|
|
|
object:view];
|
|
|
|
[nc removeObserver:view
|
|
|
|
name:NSApplicationDidChangeScreenParametersNotification
|
|
|
|
object:NSApp];
|
|
|
|
[view removeFromSuperview];
|
|
|
|
[view release];
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
[window updateForGLSubviews];
|
2013-03-06 11:59:07 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
2016-05-13 01:50:41 +02:00
|
|
|
* macdrv_set_view_frame
|
2013-03-06 11:59:07 +01:00
|
|
|
*/
|
2016-05-13 01:50:41 +02:00
|
|
|
void macdrv_set_view_frame(macdrv_view v, CGRect rect)
|
2013-03-06 11:59:07 +01:00
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
|
|
|
|
if (CGRectIsNull(rect)) rect = CGRectZero;
|
|
|
|
|
2017-02-01 17:12:29 +01:00
|
|
|
OnMainThreadAsync(^{
|
2016-05-05 20:53:36 +02:00
|
|
|
NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
|
2013-03-06 11:59:07 +01:00
|
|
|
NSRect oldFrame = [view frame];
|
|
|
|
|
|
|
|
if (!NSEqualRects(oldFrame, newFrame))
|
|
|
|
{
|
2016-05-13 01:50:41 +02:00
|
|
|
[[view superview] setNeedsDisplayInRect:oldFrame];
|
2013-03-06 11:59:07 +01:00
|
|
|
if (NSEqualPoints(oldFrame.origin, newFrame.origin))
|
|
|
|
[view setFrameSize:newFrame.size];
|
|
|
|
else if (NSEqualSizes(oldFrame.size, newFrame.size))
|
|
|
|
[view setFrameOrigin:newFrame.origin];
|
|
|
|
else
|
|
|
|
[view setFrame:newFrame];
|
|
|
|
[view setNeedsDisplay:YES];
|
2016-05-05 20:53:36 +02:00
|
|
|
|
|
|
|
if (retina_enabled)
|
|
|
|
{
|
|
|
|
int backing_size[2] = { 0 };
|
|
|
|
[view wine_setBackingSize:backing_size];
|
|
|
|
}
|
winemac: Add a new registry setting, OpenGLSurfaceMode, to control how GL surfaces relate to the window.
The default behavior is that GL surfaces are on top of all non-GL content in
the window. This maximizes the performance for the common case of games, but
clipping by parents, siblings, and child windows isn't respected.
Setting OpenGLSurfaceMode to "behind" pushes the GL surface to be behind the
Mac window. The window has transparent holes punched through it so that the GL
surface shows through. USER32 and the wineserver take care of making sure the
holes are only where the GL windows would be unclipped and unoccluded. Because
the OS X window server has to composite the GL surface with the window, this
limits the framerate.
Since the Mac driver has no server-side rendering path, GDI rendering to a
window which has a GL surface doesn't work. As a partial workaround, mostly
for cases where a GL surface is created but never used, setting
OpenGLSurfaceMode to "transparent" allows the GDI rendering to show through the
transparent parts of the GL surface. The GDI rendering is drawn to the
top-level window's surface as normal. (The behavior of user32 to exclude the
portion covered by a GL window from GDI rendering is disabled.) The GL surface
is in front of the window but potentially wholly or partially transparent. It
is composited with the window behind it.
The GL surface is initially cleared to be completely transparent. So, if
no GL rendering is done, the window will appear as though the GL surface didn't
exist.
2015-09-15 03:35:25 +02:00
|
|
|
[(WineWindow*)[view window] updateForGLSubviews];
|
2016-05-13 01:50:41 +02:00
|
|
|
}
|
2013-03-06 11:59:07 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
2016-05-13 01:50:40 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_view_superview
|
|
|
|
*
|
|
|
|
* Move a view to a new superview and position it relative to its
|
|
|
|
* siblings. If p is non-NULL, the view is ordered behind it.
|
|
|
|
* Otherwise, the view is ordered above n. If s is NULL, use the
|
|
|
|
* content view of w as the new superview.
|
|
|
|
*/
|
|
|
|
void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
WineContentView* superview = (WineContentView*)s;
|
|
|
|
WineWindow* window = (WineWindow*)w;
|
|
|
|
WineContentView* prev = (WineContentView*)p;
|
|
|
|
WineContentView* next = (WineContentView*)n;
|
|
|
|
|
|
|
|
if (!superview)
|
|
|
|
superview = [window contentView];
|
|
|
|
|
2017-02-01 17:12:29 +01:00
|
|
|
OnMainThreadAsync(^{
|
2016-05-13 01:50:40 +02:00
|
|
|
if (superview == [view superview])
|
|
|
|
{
|
|
|
|
NSArray* subviews = [superview subviews];
|
|
|
|
NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
|
2016-06-03 07:06:49 +02:00
|
|
|
if (!prev && !next && index == [subviews count] - 1)
|
2016-05-13 01:50:40 +02:00
|
|
|
return;
|
2016-06-03 07:06:49 +02:00
|
|
|
if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
|
2016-05-13 01:50:40 +02:00
|
|
|
return;
|
2016-06-03 07:06:49 +02:00
|
|
|
if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
|
2016-05-13 01:50:40 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
WineWindow* oldWindow = (WineWindow*)[view window];
|
|
|
|
WineWindow* newWindow = (WineWindow*)[superview window];
|
|
|
|
|
2016-07-22 08:25:06 +02:00
|
|
|
#if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
|
|
|
|
if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
|
|
|
|
[view removeFromSuperview];
|
|
|
|
#endif
|
2016-05-13 01:50:40 +02:00
|
|
|
if (prev)
|
|
|
|
[superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
|
|
|
|
else
|
|
|
|
[superview addSubview:view positioned:NSWindowAbove relativeTo:next];
|
|
|
|
|
|
|
|
if (oldWindow != newWindow)
|
|
|
|
{
|
|
|
|
[oldWindow updateForGLSubviews];
|
|
|
|
[newWindow updateForGLSubviews];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
2016-05-13 01:50:42 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_set_view_hidden
|
|
|
|
*/
|
|
|
|
void macdrv_set_view_hidden(macdrv_view v, int hidden)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
|
2017-02-01 17:12:29 +01:00
|
|
|
OnMainThreadAsync(^{
|
2016-05-13 01:50:42 +02:00
|
|
|
[view setHidden:hidden];
|
2017-02-02 22:16:27 +01:00
|
|
|
[(WineWindow*)view.window updateForGLSubviews];
|
2016-05-13 01:50:42 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
2013-03-06 11:59:07 +01:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_add_view_opengl_context
|
|
|
|
*
|
|
|
|
* Add an OpenGL context to the list being tracked for each view.
|
|
|
|
*/
|
|
|
|
void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
WineOpenGLContext *context = (WineOpenGLContext*)c;
|
|
|
|
|
2013-11-27 22:32:46 +01:00
|
|
|
OnMainThread(^{
|
2013-03-06 11:59:07 +01:00
|
|
|
[view addGLContext:context];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_remove_view_opengl_context
|
|
|
|
*
|
|
|
|
* Add an OpenGL context to the list being tracked for each view.
|
|
|
|
*/
|
|
|
|
void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
WineOpenGLContext *context = (WineOpenGLContext*)c;
|
|
|
|
|
|
|
|
OnMainThreadAsync(^{
|
|
|
|
[view removeGLContext:context];
|
|
|
|
});
|
|
|
|
|
|
|
|
[pool release];
|
|
|
|
}
|
2013-04-04 21:26:08 +02:00
|
|
|
|
2018-07-13 16:27:44 +02:00
|
|
|
#ifdef HAVE_METAL_METAL_H
|
|
|
|
macdrv_metal_device macdrv_create_metal_device(void)
|
|
|
|
{
|
|
|
|
macdrv_metal_device ret;
|
|
|
|
|
|
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
|
|
|
|
if (MTLCreateSystemDefaultDevice == NULL)
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
|
|
|
|
[pool release];
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void macdrv_release_metal_device(macdrv_metal_device d)
|
|
|
|
{
|
|
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
[(id<MTLDevice>)d release];
|
|
|
|
[pool release];
|
|
|
|
}
|
|
|
|
|
|
|
|
macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
|
|
|
|
{
|
|
|
|
id<MTLDevice> device = (id<MTLDevice>)d;
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
__block WineMetalView *metalView;
|
|
|
|
|
|
|
|
OnMainThread(^{
|
|
|
|
metalView = [view newMetalViewWithDevice:device];
|
|
|
|
});
|
|
|
|
|
|
|
|
return (macdrv_metal_view)metalView;
|
|
|
|
}
|
|
|
|
|
|
|
|
void macdrv_view_release_metal_view(macdrv_metal_view v)
|
|
|
|
{
|
|
|
|
WineMetalView* view = (WineMetalView*)v;
|
|
|
|
OnMainThread(^{
|
|
|
|
[view removeFromSuperview];
|
|
|
|
[view release];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-05 20:53:36 +02:00
|
|
|
int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
|
|
|
|
{
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
|
|
|
|
if (![view isKindOfClass:[WineContentView class]])
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
[view wine_getBackingSize:backing_size];
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
|
|
|
|
{
|
|
|
|
WineContentView* view = (WineContentView*)v;
|
|
|
|
|
|
|
|
if ([view isKindOfClass:[WineContentView class]])
|
|
|
|
[view wine_setBackingSize:backing_size];
|
|
|
|
}
|
|
|
|
|
2013-04-04 21:26:08 +02:00
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_window_background_color
|
|
|
|
*
|
|
|
|
* Returns the standard Mac window background color as a 32-bit value of
|
|
|
|
* the form 0x00rrggbb.
|
|
|
|
*/
|
|
|
|
uint32_t macdrv_window_background_color(void)
|
|
|
|
{
|
|
|
|
static uint32_t result;
|
|
|
|
static dispatch_once_t once;
|
|
|
|
|
|
|
|
// Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
|
|
|
|
// color spaces (RGB or grayscale). So, the only way to get RGB values out
|
|
|
|
// of it is to draw with it.
|
|
|
|
dispatch_once(&once, ^{
|
|
|
|
OnMainThread(^{
|
|
|
|
unsigned char rgbx[4];
|
|
|
|
unsigned char *planes = rgbx;
|
|
|
|
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
|
|
|
|
pixelsWide:1
|
|
|
|
pixelsHigh:1
|
|
|
|
bitsPerSample:8
|
|
|
|
samplesPerPixel:3
|
|
|
|
hasAlpha:NO
|
|
|
|
isPlanar:NO
|
|
|
|
colorSpaceName:NSCalibratedRGBColorSpace
|
|
|
|
bitmapFormat:0
|
|
|
|
bytesPerRow:4
|
|
|
|
bitsPerPixel:32];
|
|
|
|
[NSGraphicsContext saveGraphicsState];
|
|
|
|
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
|
|
|
|
[[NSColor windowBackgroundColor] set];
|
|
|
|
NSRectFill(NSMakeRect(0, 0, 1, 1));
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
|
|
[bitmap release];
|
|
|
|
result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2013-04-22 04:32:26 +02:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* macdrv_send_text_input_event
|
|
|
|
*/
|
2016-02-05 00:18:25 +01:00
|
|
|
void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
|
2013-04-22 04:32:26 +02:00
|
|
|
{
|
2016-02-05 00:18:25 +01:00
|
|
|
OnMainThreadAsync(^{
|
|
|
|
BOOL ret;
|
|
|
|
macdrv_event* event;
|
2013-04-22 04:32:26 +02:00
|
|
|
WineWindow* window = (WineWindow*)[NSApp keyWindow];
|
|
|
|
if (![window isKindOfClass:[WineWindow class]])
|
|
|
|
{
|
|
|
|
window = (WineWindow*)[NSApp mainWindow];
|
2013-05-17 01:43:16 +02:00
|
|
|
if (![window isKindOfClass:[WineWindow class]])
|
|
|
|
window = [[WineApplicationController sharedController] frontWineWindow];
|
2013-04-22 04:32:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (window)
|
|
|
|
{
|
|
|
|
NSUInteger localFlags = flags;
|
|
|
|
CGEventRef c;
|
|
|
|
NSEvent* event;
|
|
|
|
|
|
|
|
window.imeData = data;
|
|
|
|
fix_device_modifiers_by_generic(&localFlags);
|
|
|
|
|
|
|
|
// An NSEvent created with +keyEventWithType:... is internally marked
|
|
|
|
// as synthetic and doesn't get sent through input methods. But one
|
|
|
|
// created from a CGEvent doesn't have that problem.
|
|
|
|
c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
|
|
|
|
CGEventSetFlags(c, localFlags);
|
|
|
|
CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
|
|
|
|
event = [NSEvent eventWithCGEvent:c];
|
|
|
|
CFRelease(c);
|
|
|
|
|
|
|
|
window.commandDone = FALSE;
|
|
|
|
ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret = FALSE;
|
|
|
|
|
2016-02-05 00:18:25 +01:00
|
|
|
event = macdrv_create_event(SENT_TEXT_INPUT, window);
|
|
|
|
event->sent_text_input.handled = ret;
|
|
|
|
event->sent_text_input.done = done;
|
|
|
|
[[window queue] postEvent:event];
|
|
|
|
macdrv_release_event(event);
|
|
|
|
});
|
2013-04-22 04:32:26 +02:00
|
|
|
}
|
2019-05-06 18:30:14 +02:00
|
|
|
|
|
|
|
void macdrv_clear_ime_text(void)
|
|
|
|
{
|
|
|
|
OnMainThreadAsync(^{
|
|
|
|
WineWindow* window = (WineWindow*)[NSApp keyWindow];
|
|
|
|
if (![window isKindOfClass:[WineWindow class]])
|
|
|
|
{
|
|
|
|
window = (WineWindow*)[NSApp mainWindow];
|
|
|
|
if (![window isKindOfClass:[WineWindow class]])
|
|
|
|
window = [[WineApplicationController sharedController] frontWineWindow];
|
|
|
|
}
|
|
|
|
if (window)
|
|
|
|
[[window contentView] clearMarkedText];
|
|
|
|
});
|
|
|
|
}
|