Changeset - r24768:48450d9ea0c2
[Not reviewed]
master
0 4 0
Michael Lutz - 3 years ago 2021-01-30 18:13:29
michi@icosahedron.de
Codechange: [OSX] Split Window and back buffer creation in Cocoa video driver.
4 files changed with 135 insertions and 175 deletions:
0 comments (0 inline, 0 general)
src/video/cocoa/cocoa_v.h
Show inline comments
 
@@ -16,13 +16,12 @@
 

	
 
extern bool _cocoa_video_started;
 

	
 
@class OTTD_CocoaWindowDelegate;
 

	
 
class VideoDriver_Cocoa : public VideoDriver {
 
private:
 
	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_height;    ///< Current window height in pixel
 
	int window_pitch;
 
@@ -46,6 +45,8 @@ public:
 
	CGColorSpaceRef color_space; ///< Window color space
 
	CGContextRef cgcontext;      ///< Context reference for Quartz subdriver
 

	
 
	OTTD_CocoaWindowDelegate *delegate; //!< Window delegate object
 

	
 
public:
 
	VideoDriver_Cocoa();
 

	
 
@@ -69,7 +70,7 @@ public:
 
	/** Main game loop. */
 
	void GameLoop(); // In event.mm.
 

	
 
	bool WindowResized();
 
	void AllocateBackingStore();
 

	
 
protected:
 
	Dimension GetScreenSize() const override;
 
@@ -85,8 +86,8 @@ private:
 
	void GameSizeChanged();
 

	
 
	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 CheckPaletteAnim();
src/video/cocoa/cocoa_v.mm
Show inline comments
 
@@ -76,6 +76,12 @@ static const Dimension _default_resoluti
 
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()
 
{
 
	this->window_width  = 0;
 
@@ -88,6 +94,7 @@ VideoDriver_Cocoa::VideoDriver_Cocoa()
 

	
 
	this->window    = nil;
 
	this->cocoaview = nil;
 
	this->delegate  = nil;
 

	
 
	this->color_space = nullptr;
 
	this->cgcontext   = nullptr;
 
@@ -106,10 +113,12 @@ void VideoDriver_Cocoa::Stop()
 

	
 
	/* Release window mode resources */
 
	if (this->window != nil) [ this->window close ];
 
	[ this->cocoaview release ];
 
	[ this->delegate release ];
 

	
 
	CGContextRelease(this->cgcontext);
 
	CGColorSpaceRelease(this->color_space);
 

	
 
	CGColorSpaceRelease(this->color_space);
 
	free(this->window_buffer);
 
	free(this->pixel_buffer);
 

	
 
@@ -130,23 +139,21 @@ const char *VideoDriver_Cocoa::Start(con
 
	if (!CocoaSetupApplication()) return nullptr;
 

	
 
	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();
 

	
 
	if (bpp != 8 && bpp != 32) {
 
		Stop();
 
		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();
 
		return "Could not create subdriver";
 
		return "Could not create window";
 
	}
 

	
 
	if (_fullscreen) this->ToggleFullscreen(_fullscreen);
 
	if (fullscreen) this->ToggleFullscreen(fullscreen);
 

	
 
	this->GameSizeChanged();
 
	this->UpdateVideoModes();
 
@@ -195,17 +202,29 @@ void VideoDriver_Cocoa::MainLoop()
 
 */
 
bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
 
{
 
	int old_width  = this->window_width;
 
	int old_height = this->window_height;
 
	int old_bpp    = this->buffer_depth;
 
	NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
 
	w = std::min(w, (int)screen_size.width);
 
	h = std::min(h, (int)screen_size.height);
 

	
 
	NSRect contentRect = NSMakeRect(0, 0, w, h);
 
	[ this->window setContentSize:contentRect.size ];
 

	
 
	if (this->SetVideoMode(w, h, BlitterFactory::GetCurrentBlitter()->GetScreenDepth())) {
 
		this->GameSizeChanged();
 
		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);
 
	return false;
 
	this->window_width = w;
 
	this->window_height = h;
 

	
 
	[ (OTTD_CocoaWindow *)this->window center ];
 
	this->AllocateBackingStore();
 

	
 
	return true;
 
}
 

	
 
/**
 
@@ -234,7 +253,9 @@ bool VideoDriver_Cocoa::ToggleFullscreen
 
 */
 
bool VideoDriver_Cocoa::AfterBlitterChange()
 
{
 
	return this->ChangeResolution(_screen.width, _screen.height);
 
	this->ChangeResolution(_screen.width, _screen.height);
 
	this->UpdatePalette(0, 256);
 
	return true;
 
}
 

	
 
/**
 
@@ -257,6 +278,15 @@ Dimension VideoDriver_Cocoa::GetScreenSi
 
}
 

	
 
/**
 
 * 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.
 
 */
 
void VideoDriver_Cocoa::GameSizeChanged()
 
@@ -277,19 +307,10 @@ void VideoDriver_Cocoa::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
 

	
 
- (void)setDriver:(VideoDriver_Cocoa *)drv
 
{
 
	driver = drv;
 
}
 
- (void)drawRect:(NSRect)invalidRect
 
{
 
	if (driver->cgcontext == NULL) return;
 
@@ -391,141 +412,85 @@ 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
 
 * @return whether fullscreen mode is currently used
 
 * Build window and view with a given size.
 
 * @param width Window width.
 
 * @param height Window height.
 
 */
 
bool VideoDriver_Cocoa::IsFullscreen()
 
{
 
	return this->window != nil && ([ this->window styleMask ] & NSWindowStyleMaskFullScreen) != 0;
 
}
 

	
 
bool VideoDriver_Cocoa::SetVideoMode(int width, int height, int bpp)
 
bool VideoDriver_Cocoa::MakeWindow(int width, int height)
 
{
 
	this->setup = true;
 
	this->GetDeviceInfo();
 

	
 
	if (width > this->device_width) width = this->device_width;
 
	if (height > this->device_height) height = this->device_height;
 
	NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
 
	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);
 

	
 
	/* Check if we should recreate the window */
 
	/* Create main window. */
 
	unsigned int style = NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask;
 
	this->window = [ [ OTTD_CocoaWindow alloc ] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:NO ];
 
	if (this->window == nil) {
 
		OTTD_CocoaWindowDelegate *delegate;
 

	
 
		/* 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) {
 
			DEBUG(driver, 0, "Could not create the Cocoa window.");
 
			this->setup = false;
 
			return false;
 
		}
 

	
 
		/* 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
 
		 * with the quartz fullscreen support as found only in 10.7 and later
 
		 */
 
		if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) {
 
			NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ];
 
			behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
 
			[ this->window setCollectionBehavior:behavior ];
 
		DEBUG(driver, 0, "Could not create the Cocoa window.");
 
		this->setup = false;
 
		return false;
 
	}
 
	[ this->window setDriver:this ];
 

	
 
			NSButton* fullscreenButton = [ this->window standardWindowButton:NSWindowFullScreenButton ];
 
			[ fullscreenButton setAction:@selector(toggleFullScreen:) ];
 
			[ fullscreenButton setTarget:this->window ];
 
		}
 

	
 
		[ this->window setDriver:this ];
 

	
 
		char caption[50];
 
		snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
 
		NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ];
 
		[ this->window setTitle:nsscaption ];
 
		[ this->window setMiniwindowTitle:nsscaption ];
 
		[ nsscaption release ];
 

	
 
		[ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ];
 
	/* 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
 
	 * with the quartz fullscreen support as found only in 10.7 and later
 
	 */
 
	if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) {
 
		NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ];
 
		behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
 
		[ this->window setCollectionBehavior:behavior ];
 

	
 
		[ this->window setAcceptsMouseMovedEvents:YES ];
 
		[ 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 ];
 
		}
 
		NSButton* fullscreenButton = [ this->window standardWindowButton:NSWindowFullScreenButton ];
 
		[ fullscreenButton setAction:@selector(toggleFullScreen:) ];
 
		[ fullscreenButton setTarget:this->window ];
 
	}
 

	
 
	this->window_width = width;
 
	this->window_height = height;
 
	this->buffer_depth = bpp;
 
	char caption[50];
 
	snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
 
	NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ];
 
	[ this->window setTitle:nsscaption ];
 
	[ this->window setMiniwindowTitle:nsscaption ];
 
	[ nsscaption release ];
 

	
 
	[ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ];
 

	
 
	[ (OTTD_CocoaWindow *)this->window center ];
 
	this->delegate = [ [ OTTD_CocoaWindowDelegate alloc ] initWithDriver:this ];
 
	[ this->window setDelegate:this->delegate ];
 

	
 
	/* Only recreate the view if it doesn't already exist */
 
	[ this->window setAcceptsMouseMovedEvents:YES ];
 
	[ this->window setViewsNeedDisplay:NO ];
 
	[ (OTTD_CocoaWindow *)this->window center ];
 
	[ this->window makeKeyAndOrderFront:nil ];
 

	
 
	/* Create content view. */
 
	this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:[ this->window contentRectForFrameRect:[ this->window frame ] ] andDriver:this ];
 
	if (this->cocoaview == nil) {
 
		this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:contentRect ];
 
		if (this->cocoaview == nil) {
 
			DEBUG(driver, 0, "Could not create the Quartz view.");
 
			this->setup = false;
 
			return false;
 
		}
 

	
 
		[ this->cocoaview setDriver:this ];
 

	
 
		[ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
 
		[ this->window setContentView:cocoaview ];
 
		[ this->cocoaview release ];
 
		[ this->window makeKeyAndOrderFront:nil ];
 
		DEBUG(driver, 0, "Could not create the Quartz view.");
 
		this->setup = false;
 
		return false;
 
	}
 

	
 
	[ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
 
	[ this->window setContentView:cocoaview ];
 

	
 
	[ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ];
 
	CGColorSpaceRelease(this->color_space);
 
	this->color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
 
	if (this->color_space == nullptr) this->color_space = CGColorSpaceCreateDeviceRGB();
 
	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;
 

	
 
	return ret;
 
	this->UpdatePalette(0, 256);
 
	this->AllocateBackingStore();
 

	
 
	return true;
 
}
 

	
 
/**
 
@@ -624,7 +589,7 @@ CGPoint VideoDriver_Cocoa::PrivateLocalT
 
	*p = [ this->cocoaview convertPoint:*p toView:nil ];
 
	*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;
 
	cgp.x = p->x;
 
@@ -674,18 +639,16 @@ static void ClearWindowBuffer(uint32 *bu
 
	}
 
}
 

	
 
/**
 
 * Resize the window.
 
 * @return whether the window was successfully resized
 
 */
 
bool VideoDriver_Cocoa::WindowResized()
 
/** Resize the window. */
 
void VideoDriver_Cocoa::AllocateBackingStore()
 
{
 
	if (this->window == nil || this->cocoaview == nil) return true;
 
	if (this->window == nil || this->cocoaview == nil || this->setup) return;
 

	
 
	NSRect newframe = [ this->cocoaview frame ];
 

	
 
	this->window_width = (int)newframe.size.width;
 
	this->window_height = (int)newframe.size.height;
 
	this->buffer_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 

	
 
	/* Create Core Graphics Context */
 
	free(this->window_buffer);
 
@@ -712,18 +675,12 @@ bool VideoDriver_Cocoa::WindowResized()
 
	if (this->buffer_depth == 8) {
 
		free(this->pixel_buffer);
 
		this->pixel_buffer = malloc(this->window_width * this->window_height);
 
		if (this->pixel_buffer == NULL) {
 
			DEBUG(driver, 0, "Failed to allocate pixel buffer");
 
			return false;
 
		}
 
		if (this->pixel_buffer == nullptr) usererror("Out of memory allocating pixel buffer");
 
	}
 

	
 
	this->GameSizeChanged();
 

	
 
	/* Redraw screen */
 
	this->num_dirty_rects = lengthof(this->dirty_rects);
 

	
 
	return true;
 
	this->GameSizeChanged();
 
}
 

	
 
void VideoDriver_Cocoa::CheckPaletteAnim()
src/video/cocoa/cocoa_wnd.h
Show inline comments
 
@@ -42,7 +42,7 @@ extern NSString *OTTDMainLaunchGameEngin
 
	VideoDriver_Cocoa *driver;
 
	NSTrackingRectTag trackingtag;
 
}
 
- (void)setDriver:(VideoDriver_Cocoa *)drv;
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv;
 
- (void)drawRect:(NSRect)rect;
 
- (BOOL)isOpaque;
 
- (BOOL)acceptsFirstResponder;
 
@@ -61,12 +61,11 @@ extern NSString *OTTDMainLaunchGameEngin
 
{
 
	VideoDriver_Cocoa *driver;
 
}
 

	
 
- (void)setDriver:(VideoDriver_Cocoa *)drv;
 
- (instancetype)initWithDriver:(VideoDriver_Cocoa *)drv;
 

	
 
- (BOOL)windowShouldClose:(id)sender;
 
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification;
 
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification;
 
- (void)windowDidChangeBackingProperties:(NSNotification *)notification;
 
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
 
@end
 

	
src/video/cocoa/cocoa_wnd.mm
Show inline comments
 
@@ -324,10 +324,7 @@ void CocoaDialog(const char *title, cons
 
{
 
	[ super setFrame:frameRect display:flag ];
 

	
 
	/* Don't do anything if the window is currently being created */
 
	if (driver->setup) return;
 

	
 
	if (!driver->WindowResized()) error("Cocoa: Failed to resize window.");
 
	driver->AllocateBackingStore();
 
}
 
/**
 
 * Handle hiding of the application
 
@@ -401,12 +398,13 @@ static const char *Utf8AdvanceByUtf16Uni
 
}
 

	
 
@implementation OTTD_CocoaView
 
/**
 
 * Initialize the driver
 
 */
 
- (void)setDriver:(VideoDriver_Cocoa *)drv
 

	
 
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv
 
{
 
	driver = drv;
 
	if (self = [ super initWithFrame:frameRect ]) {
 
		self->driver = drv;
 
	}
 
	return self;
 
}
 
/**
 
 * Define the opaqueness of the window / screen
 
@@ -810,9 +808,12 @@ static const char *Utf8AdvanceByUtf16Uni
 

	
 
@implementation OTTD_CocoaWindowDelegate
 
/** 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 */
 
- (BOOL)windowShouldClose:(id)sender
 
@@ -854,10 +855,11 @@ static const char *Utf8AdvanceByUtf16Uni
 
		[ e release ];
 
	}
 
}
 
/** The colour profile of the screen the window is on changed. */
 
- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification
 
/** Screen the window is on changed. */
 
- (void)windowDidChangeBackingProperties:(NSNotification *)notification
 
{
 
	if (!driver->setup) driver->WindowResized();
 
	/* Reallocate screen buffer if necessary. */
 
	driver->AllocateBackingStore();
 
}
 

	
 
/** Presentation options to use for fullsreen mode. */
 
@@ -867,4 +869,5 @@ static const char *Utf8AdvanceByUtf16Uni
 
}
 

	
 
@end
 

	
 
#endif /* WITH_COCOA */
0 comments (0 inline, 0 general)