1
0
Fork 0

Codechange: [OSX] Split Window and back buffer creation in Cocoa video driver.

pull/8672/head
Michael Lutz 2021-01-30 19:13:29 +01:00
parent 0eff7de659
commit 8aaf4ea098
4 changed files with 134 additions and 174 deletions

View File

@ -16,13 +16,12 @@
extern bool _cocoa_video_started; extern bool _cocoa_video_started;
@class OTTD_CocoaWindowDelegate;
class VideoDriver_Cocoa : public VideoDriver { class VideoDriver_Cocoa : public VideoDriver {
private: private:
Dimension orig_res; ///< Saved window size for non-fullscreen mode. Dimension orig_res; ///< Saved window size for non-fullscreen mode.
int device_width; ///< Width of device in pixel
int device_height; ///< Height of device in pixel
int window_width; ///< Current window width in pixel int window_width; ///< Current window width in pixel
int window_height; ///< Current window height in pixel int window_height; ///< Current window height in pixel
int window_pitch; int window_pitch;
@ -46,6 +45,8 @@ public:
CGColorSpaceRef color_space; ///< Window color space CGColorSpaceRef color_space; ///< Window color space
CGContextRef cgcontext; ///< Context reference for Quartz subdriver CGContextRef cgcontext; ///< Context reference for Quartz subdriver
OTTD_CocoaWindowDelegate *delegate; //!< Window delegate object
public: public:
VideoDriver_Cocoa(); VideoDriver_Cocoa();
@ -69,7 +70,7 @@ public:
/** Main game loop. */ /** Main game loop. */
void GameLoop(); // In event.mm. void GameLoop(); // In event.mm.
bool WindowResized(); void AllocateBackingStore();
protected: protected:
Dimension GetScreenSize() const override; Dimension GetScreenSize() const override;
@ -85,8 +86,8 @@ private:
void GameSizeChanged(); void GameSizeChanged();
void UpdateVideoModes(); void UpdateVideoModes();
void GetDeviceInfo();
bool SetVideoMode(int width, int height, int bpp); bool MakeWindow(int width, int height);
void UpdatePalette(uint first_color, uint num_colors); void UpdatePalette(uint first_color, uint num_colors);
void CheckPaletteAnim(); void CheckPaletteAnim();

View File

@ -76,6 +76,12 @@ static const Dimension _default_resolutions[] = {
static FVideoDriver_Cocoa iFVideoDriver_Cocoa; static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
/* Subclass of OTTD_CocoaView to fix Quartz rendering */
@interface OTTD_QuartzView : OTTD_CocoaView
- (void)drawRect:(NSRect)invalidRect;
@end
VideoDriver_Cocoa::VideoDriver_Cocoa() VideoDriver_Cocoa::VideoDriver_Cocoa()
{ {
this->window_width = 0; this->window_width = 0;
@ -88,6 +94,7 @@ VideoDriver_Cocoa::VideoDriver_Cocoa()
this->window = nil; this->window = nil;
this->cocoaview = nil; this->cocoaview = nil;
this->delegate = nil;
this->color_space = nullptr; this->color_space = nullptr;
this->cgcontext = nullptr; this->cgcontext = nullptr;
@ -106,10 +113,12 @@ void VideoDriver_Cocoa::Stop()
/* Release window mode resources */ /* Release window mode resources */
if (this->window != nil) [ this->window close ]; if (this->window != nil) [ this->window close ];
[ this->cocoaview release ];
[ this->delegate release ];
CGContextRelease(this->cgcontext); CGContextRelease(this->cgcontext);
CGColorSpaceRelease(this->color_space); CGColorSpaceRelease(this->color_space);
free(this->window_buffer); free(this->window_buffer);
free(this->pixel_buffer); free(this->pixel_buffer);
@ -130,23 +139,21 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm)
if (!CocoaSetupApplication()) return nullptr; if (!CocoaSetupApplication()) return nullptr;
this->UpdateAutoResolution(); this->UpdateAutoResolution();
this->orig_res = _cur_resolution; this->orig_res = _cur_resolution;
int width = _cur_resolution.width;
int height = _cur_resolution.height;
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
if (bpp != 8 && bpp != 32) { if (bpp != 8 && bpp != 32) {
Stop(); Stop();
return "The cocoa quartz subdriver only supports 8 and 32 bpp."; return "The cocoa quartz subdriver only supports 8 and 32 bpp.";
} }
if (!this->SetVideoMode(width, height, bpp)) { bool fullscreen = _fullscreen;
if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) {
Stop(); Stop();
return "Could not create subdriver"; return "Could not create window";
} }
if (_fullscreen) this->ToggleFullscreen(_fullscreen); if (fullscreen) this->ToggleFullscreen(fullscreen);
this->GameSizeChanged(); this->GameSizeChanged();
this->UpdateVideoModes(); this->UpdateVideoModes();
@ -195,17 +202,29 @@ void VideoDriver_Cocoa::MainLoop()
*/ */
bool VideoDriver_Cocoa::ChangeResolution(int w, int h) bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
{ {
int old_width = this->window_width; NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
int old_height = this->window_height; w = std::min(w, (int)screen_size.width);
int old_bpp = this->buffer_depth; h = std::min(h, (int)screen_size.height);
if (this->SetVideoMode(w, h, BlitterFactory::GetCurrentBlitter()->GetScreenDepth())) { NSRect contentRect = NSMakeRect(0, 0, w, h);
this->GameSizeChanged(); [ this->window setContentSize:contentRect.size ];
return true;
/* Ensure frame height - title bar height >= view height */
float content_height = [ this->window contentRectForFrameRect:[ this->window frame ] ].size.height;
contentRect.size.height = Clamp(h, 0, (int)content_height);
if (this->cocoaview != nil) {
h = (int)contentRect.size.height;
[ this->cocoaview setFrameSize:contentRect.size ];
} }
if (old_width != 0 && old_height != 0) this->SetVideoMode(old_width, old_height, old_bpp); this->window_width = w;
return false; this->window_height = h;
[ (OTTD_CocoaWindow *)this->window center ];
this->AllocateBackingStore();
return true;
} }
/** /**
@ -234,7 +253,9 @@ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
*/ */
bool VideoDriver_Cocoa::AfterBlitterChange() bool VideoDriver_Cocoa::AfterBlitterChange()
{ {
return this->ChangeResolution(_screen.width, _screen.height); this->ChangeResolution(_screen.width, _screen.height);
this->UpdatePalette(0, 256);
return true;
} }
/** /**
@ -256,6 +277,15 @@ Dimension VideoDriver_Cocoa::GetScreenSize() const
return { static_cast<uint>(NSWidth(frame)), static_cast<uint>(NSHeight(frame)) }; return { static_cast<uint>(NSWidth(frame)), static_cast<uint>(NSHeight(frame)) };
} }
/**
* Are we in fullscreen mode
* @return whether fullscreen mode is currently used
*/
bool VideoDriver_Cocoa::IsFullscreen()
{
return this->window != nil && ([ this->window styleMask ] & NSWindowStyleMaskFullScreen) != 0;
}
/** /**
* Handle a change of the display area. * Handle a change of the display area.
*/ */
@ -277,19 +307,10 @@ void VideoDriver_Cocoa::GameSizeChanged()
::GameSizeChanged(); ::GameSizeChanged();
} }
/* Subclass of OTTD_CocoaView to fix Quartz rendering */
@interface OTTD_QuartzView : OTTD_CocoaView
- (void)setDriver:(VideoDriver_Cocoa *)drv;
- (void)drawRect:(NSRect)invalidRect;
@end
@implementation OTTD_QuartzView @implementation OTTD_QuartzView
- (void)setDriver:(VideoDriver_Cocoa *)drv
{
driver = drv;
}
- (void)drawRect:(NSRect)invalidRect - (void)drawRect:(NSRect)invalidRect
{ {
if (driver->cgcontext == NULL) return; if (driver->cgcontext == NULL) return;
@ -391,61 +412,30 @@ void VideoDriver_Cocoa::UpdateVideoModes()
} }
} }
void VideoDriver_Cocoa::GetDeviceInfo()
{
/* Initialize the video settings; this data persists between mode switches
* and gather some information that is useful to know about the display */
/* Use the new API when compiling for OSX 10.6 or later */
CGDisplayModeRef cur_mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay);
if (cur_mode == NULL) { return; }
this->device_width = CGDisplayModeGetWidth(cur_mode);
this->device_height = CGDisplayModeGetHeight(cur_mode);
CGDisplayModeRelease(cur_mode);
}
/** /**
* Are we in fullscreen mode * Build window and view with a given size.
* @return whether fullscreen mode is currently used * @param width Window width.
* @param height Window height.
*/ */
bool VideoDriver_Cocoa::IsFullscreen() bool VideoDriver_Cocoa::MakeWindow(int width, int height)
{
return this->window != nil && ([ this->window styleMask ] & NSWindowStyleMaskFullScreen) != 0;
}
bool VideoDriver_Cocoa::SetVideoMode(int width, int height, int bpp)
{ {
this->setup = true; this->setup = true;
this->GetDeviceInfo();
if (width > this->device_width) width = this->device_width; NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
if (height > this->device_height) height = this->device_height; if (width > screen_size.width) width = screen_size.width;
if (height > screen_size.height) height = screen_size.height;
NSRect contentRect = NSMakeRect(0, 0, width, height); NSRect contentRect = NSMakeRect(0, 0, width, height);
/* Check if we should recreate the window */ /* Create main window. */
if (this->window == nil) { unsigned int style = NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask;
OTTD_CocoaWindowDelegate *delegate; this->window = [ [ OTTD_CocoaWindow alloc ] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:NO ];
/* Set the window style */
unsigned int style = NSTitledWindowMask;
style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
style |= NSResizableWindowMask;
/* Manually create a window, avoids having a nib file resource */
this->window = [ [ OTTD_CocoaWindow alloc ]
initWithContentRect:contentRect
styleMask:style
backing:NSBackingStoreBuffered
defer:NO ];
if (this->window == nil) { if (this->window == nil) {
DEBUG(driver, 0, "Could not create the Cocoa window."); DEBUG(driver, 0, "Could not create the Cocoa window.");
this->setup = false; this->setup = false;
return false; return false;
} }
[ this->window setDriver:this ];
/* Add built in full-screen support when available (OS X 10.7 and higher) /* Add built in full-screen support when available (OS X 10.7 and higher)
* This code actually compiles for 10.5 and later, but only makes sense in conjunction * This code actually compiles for 10.5 and later, but only makes sense in conjunction
@ -461,8 +451,6 @@ bool VideoDriver_Cocoa::SetVideoMode(int width, int height, int bpp)
[ fullscreenButton setTarget:this->window ]; [ fullscreenButton setTarget:this->window ];
} }
[ this->window setDriver:this ];
char caption[50]; char caption[50];
snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ]; NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ];
@ -472,60 +460,37 @@ bool VideoDriver_Cocoa::SetVideoMode(int width, int height, int bpp)
[ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; [ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ];
this->delegate = [ [ OTTD_CocoaWindowDelegate alloc ] initWithDriver:this ];
[ this->window setDelegate:this->delegate ];
[ this->window setAcceptsMouseMovedEvents:YES ]; [ this->window setAcceptsMouseMovedEvents:YES ];
[ this->window setViewsNeedDisplay:NO ]; [ this->window setViewsNeedDisplay:NO ];
delegate = [ [ OTTD_CocoaWindowDelegate alloc ] init ];
[ delegate setDriver:this ];
[ this->window setDelegate:[ delegate autorelease ] ];
} else {
/* We already have a window, just change its size */
[ this->window setContentSize:contentRect.size ];
/* Ensure frame height - title bar height >= view height */
float content_height = [ this->window contentRectForFrameRect:[ this->window frame ] ].size.height;
contentRect.size.height = Clamp(height, 0, (int)content_height);
if (this->cocoaview != nil) {
height = (int)contentRect.size.height;
[ this->cocoaview setFrameSize:contentRect.size ];
}
}
this->window_width = width;
this->window_height = height;
this->buffer_depth = bpp;
[ (OTTD_CocoaWindow *)this->window center ]; [ (OTTD_CocoaWindow *)this->window center ];
[ this->window makeKeyAndOrderFront:nil ];
/* Only recreate the view if it doesn't already exist */ /* Create content view. */
if (this->cocoaview == nil) { this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:[ this->window contentRectForFrameRect:[ this->window frame ] ] andDriver:this ];
this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:contentRect ];
if (this->cocoaview == nil) { if (this->cocoaview == nil) {
DEBUG(driver, 0, "Could not create the Quartz view."); DEBUG(driver, 0, "Could not create the Quartz view.");
this->setup = false; this->setup = false;
return false; return false;
} }
[ this->cocoaview setDriver:this ];
[ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ]; [ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
[ this->window setContentView:cocoaview ]; [ this->window setContentView:cocoaview ];
[ this->cocoaview release ];
[ this->window makeKeyAndOrderFront:nil ];
}
[ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ]; [ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ];
CGColorSpaceRelease(this->color_space);
this->color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); this->color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
if (this->color_space == nullptr) this->color_space = CGColorSpaceCreateDeviceRGB(); if (this->color_space == nullptr) this->color_space = CGColorSpaceCreateDeviceRGB();
if (this->color_space == nullptr) error("Could not get a valid colour space for drawing."); if (this->color_space == nullptr) error("Could not get a valid colour space for drawing.");
bool ret = this->WindowResized();
this->UpdatePalette(0, 256);
this->setup = false; this->setup = false;
return ret; this->UpdatePalette(0, 256);
this->AllocateBackingStore();
return true;
} }
/** /**
@ -624,7 +589,7 @@ CGPoint VideoDriver_Cocoa::PrivateLocalToCG(NSPoint *p)
*p = [ this->cocoaview convertPoint:*p toView:nil ]; *p = [ this->cocoaview convertPoint:*p toView:nil ];
*p = [ this->window convertRectToScreen:NSMakeRect(p->x, p->y, 0, 0) ].origin; *p = [ this->window convertRectToScreen:NSMakeRect(p->x, p->y, 0, 0) ].origin;
p->y = this->device_height - p->y; p->y = NSScreen.screens[0].frame.size.height - p->y;
CGPoint cgp; CGPoint cgp;
cgp.x = p->x; cgp.x = p->x;
@ -674,18 +639,16 @@ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
} }
} }
/** /** Resize the window. */
* Resize the window. void VideoDriver_Cocoa::AllocateBackingStore()
* @return whether the window was successfully resized
*/
bool VideoDriver_Cocoa::WindowResized()
{ {
if (this->window == nil || this->cocoaview == nil) return true; if (this->window == nil || this->cocoaview == nil || this->setup) return;
NSRect newframe = [ this->cocoaview frame ]; NSRect newframe = [ this->cocoaview frame ];
this->window_width = (int)newframe.size.width; this->window_width = (int)newframe.size.width;
this->window_height = (int)newframe.size.height; this->window_height = (int)newframe.size.height;
this->buffer_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
/* Create Core Graphics Context */ /* Create Core Graphics Context */
free(this->window_buffer); free(this->window_buffer);
@ -712,18 +675,12 @@ bool VideoDriver_Cocoa::WindowResized()
if (this->buffer_depth == 8) { if (this->buffer_depth == 8) {
free(this->pixel_buffer); free(this->pixel_buffer);
this->pixel_buffer = malloc(this->window_width * this->window_height); this->pixel_buffer = malloc(this->window_width * this->window_height);
if (this->pixel_buffer == NULL) { if (this->pixel_buffer == nullptr) usererror("Out of memory allocating pixel buffer");
DEBUG(driver, 0, "Failed to allocate pixel buffer");
return false;
} }
}
this->GameSizeChanged();
/* Redraw screen */ /* Redraw screen */
this->num_dirty_rects = lengthof(this->dirty_rects); this->num_dirty_rects = lengthof(this->dirty_rects);
this->GameSizeChanged();
return true;
} }
void VideoDriver_Cocoa::CheckPaletteAnim() void VideoDriver_Cocoa::CheckPaletteAnim()

View File

@ -42,7 +42,7 @@ extern NSString *OTTDMainLaunchGameEngine;
VideoDriver_Cocoa *driver; VideoDriver_Cocoa *driver;
NSTrackingRectTag trackingtag; NSTrackingRectTag trackingtag;
} }
- (void)setDriver:(VideoDriver_Cocoa *)drv; - (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv;
- (void)drawRect:(NSRect)rect; - (void)drawRect:(NSRect)rect;
- (BOOL)isOpaque; - (BOOL)isOpaque;
- (BOOL)acceptsFirstResponder; - (BOOL)acceptsFirstResponder;
@ -61,12 +61,11 @@ extern NSString *OTTDMainLaunchGameEngine;
{ {
VideoDriver_Cocoa *driver; VideoDriver_Cocoa *driver;
} }
- (instancetype)initWithDriver:(VideoDriver_Cocoa *)drv;
- (void)setDriver:(VideoDriver_Cocoa *)drv;
- (BOOL)windowShouldClose:(id)sender; - (BOOL)windowShouldClose:(id)sender;
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification; - (void)windowDidEnterFullScreen:(NSNotification *)aNotification;
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification; - (void)windowDidChangeBackingProperties:(NSNotification *)notification;
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
@end @end

View File

@ -324,10 +324,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
{ {
[ super setFrame:frameRect display:flag ]; [ super setFrame:frameRect display:flag ];
/* Don't do anything if the window is currently being created */ driver->AllocateBackingStore();
if (driver->setup) return;
if (!driver->WindowResized()) error("Cocoa: Failed to resize window.");
} }
/** /**
* Handle hiding of the application * Handle hiding of the application
@ -401,12 +398,13 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
} }
@implementation OTTD_CocoaView @implementation OTTD_CocoaView
/**
* Initialize the driver - (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv
*/
- (void)setDriver:(VideoDriver_Cocoa *)drv
{ {
driver = drv; if (self = [ super initWithFrame:frameRect ]) {
self->driver = drv;
}
return self;
} }
/** /**
* Define the opaqueness of the window / screen * Define the opaqueness of the window / screen
@ -810,9 +808,12 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
@implementation OTTD_CocoaWindowDelegate @implementation OTTD_CocoaWindowDelegate
/** Initialize the video driver */ /** Initialize the video driver */
- (void)setDriver:(VideoDriver_Cocoa *)drv - (instancetype)initWithDriver:(VideoDriver_Cocoa *)drv
{ {
driver = drv; if (self = [ super init ]) {
self->driver = drv;
}
return self;
} }
/** Handle closure requests */ /** Handle closure requests */
- (BOOL)windowShouldClose:(id)sender - (BOOL)windowShouldClose:(id)sender
@ -854,10 +855,11 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
[ e release ]; [ e release ];
} }
} }
/** The colour profile of the screen the window is on changed. */ /** Screen the window is on changed. */
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification - (void)windowDidChangeBackingProperties:(NSNotification *)notification
{ {
if (!driver->setup) driver->WindowResized(); /* Reallocate screen buffer if necessary. */
driver->AllocateBackingStore();
} }
/** Presentation options to use for fullsreen mode. */ /** Presentation options to use for fullsreen mode. */
@ -867,4 +869,5 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
} }
@end @end
#endif /* WITH_COCOA */ #endif /* WITH_COCOA */