Merge tag '1.11.0-beta2' into jgrpp
# Conflicts: # .github/workflows/ci-build.yml # .github/workflows/release.yml # CMakeLists.txt # src/blitter/32bpp_optimized.cpp # src/debug.cpp # src/gfx.cpp # src/gfx_func.h # src/lang/czech.txt # src/lang/english.txt # src/lang/italian.txt # src/lang/swedish.txt # src/lang/ukrainian.txt # src/network/network_server.cpp # src/os/windows/crashlog_win.cpp # src/os/windows/win32.cpp # src/pathfinder/follow_track.hpp # src/screenshot.cpp # src/settings_type.h # src/spritecache.cpp # src/vehicle_gui.cpp # src/video/sdl2_v.cpp # src/video/video_driver.cpp # src/video/video_driver.hpp # src/video/win32_v.cpp
This commit is contained in:
@@ -6,3 +6,9 @@ add_files(
|
||||
cocoa_wnd.mm
|
||||
CONDITION APPLE
|
||||
)
|
||||
|
||||
add_files(
|
||||
cocoa_ogl.h
|
||||
cocoa_ogl.mm
|
||||
CONDITION APPLE AND OPENGL_FOUND
|
||||
)
|
||||
|
||||
59
src/video/cocoa/cocoa_ogl.h
Normal file
59
src/video/cocoa/cocoa_ogl.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD 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 General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file cocoa_ogl.h The Cocoa OpenGL video driver. */
|
||||
|
||||
#ifndef VIDEO_COCOA_OGL_H
|
||||
#define VIDEO_COCOA_OGL_H
|
||||
|
||||
#include "cocoa_v.h"
|
||||
|
||||
@class OTTD_OpenGLView;
|
||||
|
||||
class VideoDriver_CocoaOpenGL : public VideoDriver_Cocoa {
|
||||
CGLContextObj gl_context;
|
||||
|
||||
uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end.
|
||||
|
||||
const char *AllocateContext(bool allow_software);
|
||||
|
||||
public:
|
||||
VideoDriver_CocoaOpenGL() : gl_context(nullptr), anim_buffer(nullptr) {}
|
||||
|
||||
const char *Start(const StringList ¶m) override;
|
||||
void Stop() override;
|
||||
|
||||
bool HasEfficient8Bpp() const override { return true; }
|
||||
|
||||
bool UseSystemCursor() override { return true; }
|
||||
|
||||
void ClearSystemSprites() override;
|
||||
|
||||
bool HasAnimBuffer() override { return true; }
|
||||
uint8 *GetAnimBuffer() override { return this->anim_buffer; }
|
||||
|
||||
/** Return driver name */
|
||||
const char *GetName() const override { return "cocoa-opengl"; }
|
||||
|
||||
void AllocateBackingStore(bool force = false) override;
|
||||
|
||||
protected:
|
||||
void Paint() override;
|
||||
|
||||
void *GetVideoPointer() override;
|
||||
void ReleaseVideoPointer() override;
|
||||
|
||||
NSView* AllocateDrawView() override;
|
||||
};
|
||||
|
||||
class FVideoDriver_CocoaOpenGL : public DriverFactoryBase {
|
||||
public:
|
||||
FVideoDriver_CocoaOpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 9, "cocoa-opengl", "Cocoa OpenGL Video Driver") {}
|
||||
Driver *CreateInstance() const override { return new VideoDriver_CocoaOpenGL(); }
|
||||
};
|
||||
|
||||
#endif /* VIDEO_COCOA_OGL_H */
|
||||
317
src/video/cocoa/cocoa_ogl.mm
Normal file
317
src/video/cocoa/cocoa_ogl.mm
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD 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 General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file cocoa_ogl.mm Code related to the cocoa OpengL video driver. */
|
||||
|
||||
#ifdef WITH_COCOA
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../os/macosx/macos.h"
|
||||
|
||||
#define Rect OTTDRect
|
||||
#define Point OTTDPoint
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#undef Rect
|
||||
#undef Point
|
||||
|
||||
#include "../../openttd.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../core/geometry_func.hpp"
|
||||
#include "../../core/math_func.hpp"
|
||||
#include "../../core/mem_func.hpp"
|
||||
#include "cocoa_ogl.h"
|
||||
#include "cocoa_wnd.h"
|
||||
#include "../../blitter/factory.hpp"
|
||||
#include "../../gfx_func.h"
|
||||
#include "../../framerate_type.h"
|
||||
#include "../opengl.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
|
||||
|
||||
/**
|
||||
* Important notice regarding all modifications!!!!!!!
|
||||
* There are certain limitations because the file is objective C++.
|
||||
* gdb has limitations.
|
||||
* C++ and objective C code can't be joined in all cases (classes stuff).
|
||||
* Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
|
||||
*/
|
||||
|
||||
/** Platform-specific callback to get an OpenGL funtion pointer. */
|
||||
static OGLProc GetOGLProcAddressCallback(const char *proc)
|
||||
{
|
||||
static void *dl = nullptr;
|
||||
|
||||
if (dl == nullptr) {
|
||||
dl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY);
|
||||
}
|
||||
|
||||
return reinterpret_cast<OGLProc>(dlsym(dl, proc));
|
||||
}
|
||||
|
||||
@interface OTTD_CGLLayer : CAOpenGLLayer {
|
||||
@private
|
||||
CGLContextObj _context;
|
||||
}
|
||||
|
||||
@property (class) bool allowSoftware;
|
||||
+ (CGLPixelFormatObj)defaultPixelFormat;
|
||||
|
||||
- (instancetype)initWithContext:(CGLContextObj)context;
|
||||
@end
|
||||
|
||||
@implementation OTTD_CGLLayer
|
||||
|
||||
static bool _allowSoftware;
|
||||
+ (bool)allowSoftware
|
||||
{
|
||||
return _allowSoftware;
|
||||
}
|
||||
+ (void)setAllowSoftware:(bool)newVal
|
||||
{
|
||||
_allowSoftware = newVal;
|
||||
}
|
||||
|
||||
- (instancetype)initWithContext:(CGLContextObj)context
|
||||
{
|
||||
if (self = [ super init ]) {
|
||||
self->_context = context;
|
||||
|
||||
self.opaque = YES;
|
||||
self.magnificationFilter = kCAFilterNearest;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (CGLPixelFormatObj)defaultPixelFormat
|
||||
{
|
||||
CGLPixelFormatAttribute attribs[] = {
|
||||
kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core,
|
||||
kCGLPFAColorSize, (CGLPixelFormatAttribute)24,
|
||||
kCGLPFAAlphaSize, (CGLPixelFormatAttribute)0,
|
||||
kCGLPFADepthSize, (CGLPixelFormatAttribute)0,
|
||||
kCGLPFADoubleBuffer,
|
||||
kCGLPFAAllowOfflineRenderers,
|
||||
kCGLPFASupportsAutomaticGraphicsSwitching,
|
||||
kCGLPFANoRecovery,
|
||||
_allowSoftware ? (CGLPixelFormatAttribute)0 : kCGLPFAAccelerated,
|
||||
(CGLPixelFormatAttribute)0
|
||||
};
|
||||
|
||||
CGLPixelFormatObj pxfmt = nullptr;
|
||||
GLint numPixelFormats;
|
||||
CGLChoosePixelFormat(attribs, &pxfmt, &numPixelFormats);
|
||||
|
||||
return pxfmt;
|
||||
}
|
||||
|
||||
- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
|
||||
{
|
||||
return [ OTTD_CGLLayer defaultPixelFormat ];
|
||||
}
|
||||
|
||||
- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pf
|
||||
{
|
||||
CGLContextObj ctx;
|
||||
CGLCreateContext(pf, self->_context, &ctx);
|
||||
|
||||
/* Set context state that is not shared. */
|
||||
CGLSetCurrentContext(ctx);
|
||||
OpenGLBackend::Get()->PrepareContext();
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
- (void)drawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(nullable const CVTimeStamp *)ts
|
||||
{
|
||||
CGLSetCurrentContext(ctx);
|
||||
|
||||
OpenGLBackend::Get()->Paint();
|
||||
if (_cursor.in_window) OpenGLBackend::Get()->DrawMouseCursor();
|
||||
|
||||
[ super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts ];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface OTTD_CGLLayerView : NSView
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect context:(CGLContextObj)context;
|
||||
@end
|
||||
|
||||
@implementation OTTD_CGLLayerView
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect context:(CGLContextObj)context
|
||||
{
|
||||
if (self = [ super initWithFrame:frameRect ]) {
|
||||
/* We manage our content updates ourselves. */
|
||||
self.wantsBestResolutionOpenGLSurface = _allow_hidpi_window ? YES : NO;
|
||||
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
|
||||
|
||||
/* Create backing layer. */
|
||||
CALayer *l = [ [ OTTD_CGLLayer alloc ] initWithContext:context ];
|
||||
self.layer = l;
|
||||
self.wantsLayer = YES;
|
||||
[ l release ];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)viewDidChangeBackingProperties
|
||||
{
|
||||
self.layer.contentsScale = _allow_hidpi_window && [ self.window respondsToSelector:@selector(backingScaleFactor) ] ? [ self.window backingScaleFactor ] : 1.0f;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
static FVideoDriver_CocoaOpenGL iFVideoDriver_CocoaOpenGL;
|
||||
|
||||
|
||||
const char *VideoDriver_CocoaOpenGL::Start(const StringList ¶m)
|
||||
{
|
||||
const char *err = this->Initialize();
|
||||
if (err != nullptr) return err;
|
||||
|
||||
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
if (bpp != 8 && bpp != 32) {
|
||||
this->Stop();
|
||||
return "The cocoa OpenGL subdriver only supports 8 and 32 bpp.";
|
||||
}
|
||||
|
||||
/* Try to allocate GL context. */
|
||||
err = this->AllocateContext(GetDriverParamBool(param, "software"));
|
||||
if (err != nullptr) {
|
||||
this->Stop();
|
||||
return err;
|
||||
}
|
||||
|
||||
bool fullscreen = _fullscreen;
|
||||
if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) {
|
||||
this->Stop();
|
||||
return "Could not create window";
|
||||
}
|
||||
|
||||
this->AllocateBackingStore(true);
|
||||
|
||||
if (fullscreen) this->ToggleFullscreen(fullscreen);
|
||||
|
||||
this->GameSizeChanged();
|
||||
this->UpdateVideoModes();
|
||||
MarkWholeScreenDirty();
|
||||
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaOpenGL::Stop()
|
||||
{
|
||||
this->VideoDriver_Cocoa::Stop();
|
||||
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
OpenGLBackend::Destroy();
|
||||
CGLReleaseContext(this->gl_context);
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaOpenGL::ClearSystemSprites()
|
||||
{
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
OpenGLBackend::Get()->ClearCursorCache();
|
||||
}
|
||||
|
||||
const char *VideoDriver_CocoaOpenGL::AllocateContext(bool allow_software)
|
||||
{
|
||||
[ OTTD_CGLLayer setAllowSoftware:allow_software ];
|
||||
|
||||
CGLPixelFormatObj pxfmt = [ OTTD_CGLLayer defaultPixelFormat ];
|
||||
if (pxfmt == nullptr) return "No suitable pixel format found";
|
||||
|
||||
CGLCreateContext(pxfmt, nullptr, &this->gl_context);
|
||||
CGLDestroyPixelFormat(pxfmt);
|
||||
|
||||
if (this->gl_context == nullptr) return "Can't create a rendering context";
|
||||
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
|
||||
return OpenGLBackend::Create(&GetOGLProcAddressCallback);
|
||||
}
|
||||
|
||||
NSView *VideoDriver_CocoaOpenGL::AllocateDrawView()
|
||||
{
|
||||
return [ [ OTTD_CGLLayerView alloc ] initWithFrame:this->cocoaview.bounds context:this->gl_context ];
|
||||
}
|
||||
|
||||
/** Resize the window. */
|
||||
void VideoDriver_CocoaOpenGL::AllocateBackingStore(bool force)
|
||||
{
|
||||
if (this->window == nil || this->setup) return;
|
||||
|
||||
if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer();
|
||||
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
NSRect frame = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
|
||||
OpenGLBackend::Get()->Resize(frame.size.width, frame.size.height, force);
|
||||
_screen.dst_ptr = this->GetVideoPointer();
|
||||
this->dirty_rect = {};
|
||||
|
||||
/* Redraw screen */
|
||||
this->GameSizeChanged();
|
||||
}
|
||||
|
||||
void *VideoDriver_CocoaOpenGL::GetVideoPointer()
|
||||
{
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
if (BlitterFactory::GetCurrentBlitter()->NeedsAnimationBuffer()) {
|
||||
this->anim_buffer = OpenGLBackend::Get()->GetAnimBuffer();
|
||||
}
|
||||
return OpenGLBackend::Get()->GetVideoBuffer();
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaOpenGL::ReleaseVideoPointer()
|
||||
{
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
|
||||
if (this->anim_buffer != nullptr) OpenGLBackend::Get()->ReleaseAnimBuffer(this->dirty_rect);
|
||||
OpenGLBackend::Get()->ReleaseVideoBuffer(this->dirty_rect);
|
||||
this->dirty_rect = {};
|
||||
_screen.dst_ptr = nullptr;
|
||||
this->anim_buffer = nullptr;
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaOpenGL::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
if (_cur_palette.count_dirty != 0) {
|
||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||
|
||||
/* Always push a changed palette to OpenGL. */
|
||||
CGLSetCurrentContext(this->gl_context);
|
||||
OpenGLBackend::Get()->UpdatePalette(_cur_palette.palette, _cur_palette.first_dirty, _cur_palette.count_dirty);
|
||||
if (blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_BLITTER) {
|
||||
blitter->PaletteAnimate(_cur_palette);
|
||||
}
|
||||
|
||||
_cur_palette.count_dirty = 0;
|
||||
}
|
||||
|
||||
[ CATransaction begin ];
|
||||
[ this->cocoaview.subviews[0].layer setNeedsDisplay ];
|
||||
[ CATransaction commit ];
|
||||
}
|
||||
|
||||
#endif /* WITH_COCOA */
|
||||
@@ -24,32 +24,18 @@ class VideoDriver_Cocoa : public VideoDriver {
|
||||
private:
|
||||
Dimension orig_res; ///< Saved window size for non-fullscreen mode.
|
||||
|
||||
int window_width; ///< Current window width in pixel
|
||||
int window_height; ///< Current window height in pixel
|
||||
int window_pitch;
|
||||
|
||||
int buffer_depth; ///< Colour depth of used frame buffer
|
||||
void *pixel_buffer; ///< used for direct pixel access
|
||||
void *window_buffer; ///< Colour translation from palette to screen
|
||||
|
||||
Rect dirty_rect; ///< Region of the screen that needs redrawing.
|
||||
|
||||
uint32 palette[256]; ///< Colour Palette
|
||||
|
||||
public:
|
||||
bool setup; ///< Window is currently being created.
|
||||
|
||||
OTTD_CocoaWindow *window; ///< Pointer to window object
|
||||
OTTD_CocoaView *cocoaview; ///< Pointer to view object
|
||||
CGColorSpaceRef color_space; ///< Window color space
|
||||
CGContextRef cgcontext; ///< Context reference for Quartz subdriver
|
||||
|
||||
OTTD_CocoaWindowDelegate *delegate; //!< Window delegate object
|
||||
|
||||
public:
|
||||
VideoDriver_Cocoa();
|
||||
|
||||
const char *Start(const StringList ¶m) override;
|
||||
void Stop() override;
|
||||
void MainLoop() override;
|
||||
|
||||
@@ -61,40 +47,83 @@ public:
|
||||
|
||||
void EditBoxLostFocus() override;
|
||||
|
||||
const char *GetName() const override { return "cocoa"; }
|
||||
|
||||
/* --- The following methods should be private, but can't be due to Obj-C limitations. --- */
|
||||
|
||||
void GameLoop();
|
||||
void MainLoopReal();
|
||||
|
||||
void AllocateBackingStore();
|
||||
virtual void AllocateBackingStore(bool force = false) = 0;
|
||||
|
||||
protected:
|
||||
Rect dirty_rect; ///< Region of the screen that needs redrawing.
|
||||
bool buffer_locked; ///< Video buffer was locked by the main thread.
|
||||
|
||||
Dimension GetScreenSize() const override;
|
||||
float GetDPIScale() override;
|
||||
void InputLoop() override;
|
||||
void Paint() override;
|
||||
void CheckPaletteAnim() override;
|
||||
bool LockVideoBuffer() override;
|
||||
void UnlockVideoBuffer() override;
|
||||
bool PollEvent() override;
|
||||
|
||||
private:
|
||||
bool PollEvent();
|
||||
|
||||
bool IsFullscreen();
|
||||
void GameSizeChanged();
|
||||
|
||||
const char *Initialize();
|
||||
|
||||
void UpdateVideoModes();
|
||||
|
||||
bool MakeWindow(int width, int height);
|
||||
|
||||
void UpdatePalette(uint first_color, uint num_colors);
|
||||
virtual NSView* AllocateDrawView() = 0;
|
||||
|
||||
void BlitIndexedToView32(int left, int top, int right, int bottom);
|
||||
/** Get a pointer to the video buffer. */
|
||||
virtual void *GetVideoPointer() = 0;
|
||||
/** Hand video buffer back to the drawing backend. */
|
||||
virtual void ReleaseVideoPointer() {}
|
||||
|
||||
private:
|
||||
bool IsFullscreen();
|
||||
};
|
||||
|
||||
class FVideoDriver_Cocoa : public DriverFactoryBase {
|
||||
class VideoDriver_CocoaQuartz : public VideoDriver_Cocoa {
|
||||
private:
|
||||
int buffer_depth; ///< Colour depth of used frame buffer
|
||||
void *pixel_buffer; ///< used for direct pixel access
|
||||
void *window_buffer; ///< Colour translation from palette to screen
|
||||
|
||||
int window_width; ///< Current window width in pixel
|
||||
int window_height; ///< Current window height in pixel
|
||||
int window_pitch;
|
||||
|
||||
uint32 palette[256]; ///< Colour Palette
|
||||
|
||||
void BlitIndexedToView32(int left, int top, int right, int bottom);
|
||||
void UpdatePalette(uint first_color, uint num_colors);
|
||||
|
||||
public:
|
||||
FVideoDriver_Cocoa() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
|
||||
Driver *CreateInstance() const override { return new VideoDriver_Cocoa(); }
|
||||
CGContextRef cgcontext; ///< Context reference for Quartz subdriver
|
||||
|
||||
VideoDriver_CocoaQuartz();
|
||||
|
||||
const char *Start(const StringList ¶m) override;
|
||||
void Stop() override;
|
||||
|
||||
/** Return driver name */
|
||||
const char *GetName() const override { return "cocoa"; }
|
||||
|
||||
void AllocateBackingStore(bool force = false) override;
|
||||
|
||||
protected:
|
||||
void Paint() override;
|
||||
void CheckPaletteAnim() override;
|
||||
|
||||
NSView* AllocateDrawView() override;
|
||||
|
||||
void *GetVideoPointer() override { return this->buffer_depth == 8 ? this->pixel_buffer : this->window_buffer; }
|
||||
};
|
||||
|
||||
class FVideoDriver_CocoaQuartz : public DriverFactoryBase {
|
||||
public:
|
||||
FVideoDriver_CocoaQuartz() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
|
||||
Driver *CreateInstance() const override { return new VideoDriver_CocoaQuartz(); }
|
||||
};
|
||||
|
||||
#endif /* VIDEO_COCOA_H */
|
||||
|
||||
@@ -80,33 +80,17 @@ static const Dimension _default_resolutions[] = {
|
||||
{ 2560, 1440 }
|
||||
};
|
||||
|
||||
static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
|
||||
|
||||
|
||||
/** Subclass of NSView for drawing to screen. */
|
||||
@interface OTTD_QuartzView : NSView {
|
||||
VideoDriver_Cocoa *driver;
|
||||
}
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv;
|
||||
@end
|
||||
|
||||
|
||||
VideoDriver_Cocoa::VideoDriver_Cocoa()
|
||||
{
|
||||
this->window_width = 0;
|
||||
this->window_height = 0;
|
||||
this->window_pitch = 0;
|
||||
this->buffer_depth = 0;
|
||||
this->window_buffer = nullptr;
|
||||
this->pixel_buffer = nullptr;
|
||||
this->setup = false;
|
||||
this->buffer_locked = false;
|
||||
|
||||
this->window = nil;
|
||||
this->cocoaview = nil;
|
||||
this->delegate = nil;
|
||||
|
||||
this->color_space = nullptr;
|
||||
this->cgcontext = nullptr;
|
||||
|
||||
this->dirty_rect = {};
|
||||
}
|
||||
@@ -123,17 +107,13 @@ void VideoDriver_Cocoa::Stop()
|
||||
[ this->cocoaview release ];
|
||||
[ this->delegate release ];
|
||||
|
||||
CGContextRelease(this->cgcontext);
|
||||
CGColorSpaceRelease(this->color_space);
|
||||
|
||||
free(this->window_buffer);
|
||||
free(this->pixel_buffer);
|
||||
|
||||
_cocoa_video_started = false;
|
||||
}
|
||||
|
||||
/** Try to start Cocoa video driver. */
|
||||
const char *VideoDriver_Cocoa::Start(const StringList &parm)
|
||||
/** Common driver initialization. */
|
||||
const char *VideoDriver_Cocoa::Initialize()
|
||||
{
|
||||
if (!MacOSVersionIsAtLeast(10, 7, 0)) return "The Cocoa video driver requires Mac OS X 10.7 or later.";
|
||||
|
||||
@@ -146,23 +126,6 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm)
|
||||
this->UpdateAutoResolution();
|
||||
this->orig_res = _cur_resolution;
|
||||
|
||||
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
if (bpp != 8 && bpp != 32) {
|
||||
Stop();
|
||||
return "The cocoa quartz subdriver only supports 8 and 32 bpp.";
|
||||
}
|
||||
|
||||
bool fullscreen = _fullscreen;
|
||||
if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) {
|
||||
Stop();
|
||||
return "Could not create window";
|
||||
}
|
||||
|
||||
if (fullscreen) this->ToggleFullscreen(fullscreen);
|
||||
|
||||
this->GameSizeChanged();
|
||||
this->UpdateVideoModes();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -216,9 +179,6 @@ bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
|
||||
[ this->cocoaview setFrameSize:contentRect.size ];
|
||||
}
|
||||
|
||||
this->window_width = w;
|
||||
this->window_height = h;
|
||||
|
||||
[ (OTTD_CocoaWindow *)this->window center ];
|
||||
this->AllocateBackingStore();
|
||||
|
||||
@@ -250,7 +210,6 @@ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
|
||||
bool VideoDriver_Cocoa::AfterBlitterChange()
|
||||
{
|
||||
this->ChangeResolution(_cur_resolution.width, _cur_resolution.height);
|
||||
this->UpdatePalette(0, 256);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -279,6 +238,30 @@ float VideoDriver_Cocoa::GetDPIScale()
|
||||
return this->cocoaview != nil ? [ this->cocoaview getContentsScale ] : 1.0f;
|
||||
}
|
||||
|
||||
/** Lock video buffer for drawing if it isn't already mapped. */
|
||||
bool VideoDriver_Cocoa::LockVideoBuffer()
|
||||
{
|
||||
if (this->buffer_locked) return false;
|
||||
this->buffer_locked = true;
|
||||
|
||||
_screen.dst_ptr = this->GetVideoPointer();
|
||||
assert(_screen.dst_ptr != nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Unlock video buffer. */
|
||||
void VideoDriver_Cocoa::UnlockVideoBuffer()
|
||||
{
|
||||
if (_screen.dst_ptr != nullptr) {
|
||||
/* Hand video buffer back to the drawing backend. */
|
||||
this->ReleaseVideoPointer();
|
||||
_screen.dst_ptr = nullptr;
|
||||
}
|
||||
|
||||
this->buffer_locked = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we in fullscreen mode?
|
||||
* @return whether fullscreen mode is currently used
|
||||
@@ -293,12 +276,6 @@ bool VideoDriver_Cocoa::IsFullscreen()
|
||||
*/
|
||||
void VideoDriver_Cocoa::GameSizeChanged()
|
||||
{
|
||||
/* Tell the game that the resolution has changed */
|
||||
_screen.width = this->window_width;
|
||||
_screen.height = this->window_height;
|
||||
_screen.pitch = this->buffer_depth == 8 ? this->window_width : this->window_pitch;
|
||||
_screen.dst_ptr = this->buffer_depth == 8 ? this->pixel_buffer : this->window_buffer;
|
||||
|
||||
/* Store old window size if we entered fullscreen mode. */
|
||||
bool fullscreen = this->IsFullscreen();
|
||||
if (fullscreen && !_fullscreen) this->orig_res = _cur_resolution;
|
||||
@@ -391,7 +368,7 @@ bool VideoDriver_Cocoa::MakeWindow(int width, int height)
|
||||
[ this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
|
||||
|
||||
/* Create content view. */
|
||||
NSView *draw_view = [ [ OTTD_QuartzView alloc ] initWithFrame:[ this->cocoaview bounds ] andDriver:this ];
|
||||
NSView *draw_view = this->AllocateDrawView();
|
||||
if (draw_view == nil) {
|
||||
DEBUG(driver, 0, "Could not create the drawing view.");
|
||||
this->setup = false;
|
||||
@@ -413,171 +390,9 @@ bool VideoDriver_Cocoa::MakeWindow(int width, int height)
|
||||
|
||||
this->setup = false;
|
||||
|
||||
this->UpdatePalette(0, 256);
|
||||
this->AllocateBackingStore();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function copies 8bpp pixels to the screen buffer in 32bpp windowed mode.
|
||||
*
|
||||
* @param left The x coord for the left edge of the box to blit.
|
||||
* @param top The y coord for the top edge of the box to blit.
|
||||
* @param right The x coord for the right edge of the box to blit.
|
||||
* @param bottom The y coord for the bottom edge of the box to blit.
|
||||
*/
|
||||
void VideoDriver_Cocoa::BlitIndexedToView32(int left, int top, int right, int bottom)
|
||||
{
|
||||
const uint32 *pal = this->palette;
|
||||
const uint8 *src = (uint8*)this->pixel_buffer;
|
||||
uint32 *dst = (uint32*)this->window_buffer;
|
||||
uint width = this->window_width;
|
||||
uint pitch = this->window_pitch;
|
||||
|
||||
for (int y = top; y < bottom; y++) {
|
||||
for (int x = left; x < right; x++) {
|
||||
dst[y * pitch + x] = pal[src[y * width + x]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint window.
|
||||
* @param force_update Whether to redraw unconditionally
|
||||
*/
|
||||
void VideoDriver_Cocoa::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
/* Check if we need to do anything */
|
||||
if (IsEmptyRect(this->dirty_rect) || [ this->window isMiniaturized ]) return;
|
||||
|
||||
/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
|
||||
if (this->buffer_depth == 8) {
|
||||
BlitIndexedToView32(
|
||||
this->dirty_rect.left,
|
||||
this->dirty_rect.top,
|
||||
this->dirty_rect.right,
|
||||
this->dirty_rect.bottom
|
||||
);
|
||||
}
|
||||
|
||||
NSRect dirtyrect;
|
||||
dirtyrect.origin.x = this->dirty_rect.left;
|
||||
dirtyrect.origin.y = this->window_height - this->dirty_rect.bottom;
|
||||
dirtyrect.size.width = this->dirty_rect.right - this->dirty_rect.left;
|
||||
dirtyrect.size.height = this->dirty_rect.bottom - this->dirty_rect.top;
|
||||
|
||||
/* Notify OS X that we have new content to show. */
|
||||
[ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
|
||||
|
||||
/* Tell the OS to get our contents to screen as soon as possible. */
|
||||
[ CATransaction flush ];
|
||||
|
||||
this->dirty_rect = {};
|
||||
}
|
||||
|
||||
/** Update the palette. */
|
||||
void VideoDriver_Cocoa::UpdatePalette(uint first_color, uint num_colors)
|
||||
{
|
||||
if (this->buffer_depth != 8) return;
|
||||
|
||||
for (uint i = first_color; i < first_color + num_colors; i++) {
|
||||
uint32 clr = 0xff000000;
|
||||
clr |= (uint32)_cur_palette.palette[i].r << 16;
|
||||
clr |= (uint32)_cur_palette.palette[i].g << 8;
|
||||
clr |= (uint32)_cur_palette.palette[i].b;
|
||||
this->palette[i] = clr;
|
||||
}
|
||||
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
/** Clear buffer to opaque black. */
|
||||
static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
|
||||
{
|
||||
uint32 fill = Colour(0, 0, 0).data;
|
||||
for (uint32 y = 0; y < height; y++) {
|
||||
for (uint32 x = 0; x < pitch; x++) {
|
||||
buffer[y * pitch + x] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Resize the window. */
|
||||
void VideoDriver_Cocoa::AllocateBackingStore()
|
||||
{
|
||||
if (this->window == nil || this->cocoaview == nil || this->setup) return;
|
||||
|
||||
NSRect newframe = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
|
||||
|
||||
this->window_width = (int)newframe.size.width;
|
||||
this->window_height = (int)newframe.size.height;
|
||||
this->window_pitch = Align(this->window_width, 16 / sizeof(uint32)); // Quartz likes lines that are multiple of 16-byte.
|
||||
this->buffer_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
|
||||
/* Create Core Graphics Context */
|
||||
free(this->window_buffer);
|
||||
this->window_buffer = malloc(this->window_pitch * this->window_height * sizeof(uint32));
|
||||
/* Initialize with opaque black. */
|
||||
ClearWindowBuffer((uint32 *)this->window_buffer, this->window_pitch, this->window_height);
|
||||
|
||||
CGContextRelease(this->cgcontext);
|
||||
this->cgcontext = CGBitmapContextCreate(
|
||||
this->window_buffer, // data
|
||||
this->window_width, // width
|
||||
this->window_height, // height
|
||||
8, // bits per component
|
||||
this->window_pitch * 4, // bytes per row
|
||||
this->color_space, // color space
|
||||
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host
|
||||
);
|
||||
|
||||
assert(this->cgcontext != NULL);
|
||||
CGContextSetShouldAntialias(this->cgcontext, FALSE);
|
||||
CGContextSetAllowsAntialiasing(this->cgcontext, FALSE);
|
||||
CGContextSetInterpolationQuality(this->cgcontext, kCGInterpolationNone);
|
||||
|
||||
if (this->buffer_depth == 8) {
|
||||
free(this->pixel_buffer);
|
||||
this->pixel_buffer = malloc(this->window_width * this->window_height);
|
||||
if (this->pixel_buffer == nullptr) usererror("Out of memory allocating pixel buffer");
|
||||
} else {
|
||||
free(this->pixel_buffer);
|
||||
this->pixel_buffer = nullptr;
|
||||
}
|
||||
|
||||
/* Redraw screen */
|
||||
this->GameSizeChanged();
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
/** Check if palette updates need to be performed. */
|
||||
void VideoDriver_Cocoa::CheckPaletteAnim()
|
||||
{
|
||||
if (_cur_palette.count_dirty != 0) {
|
||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||
|
||||
switch (blitter->UsePaletteAnimation()) {
|
||||
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
||||
this->UpdatePalette(_cur_palette.first_dirty, _cur_palette.count_dirty);
|
||||
break;
|
||||
|
||||
case Blitter::PALETTE_ANIMATION_BLITTER:
|
||||
blitter->PaletteAnimate(_cur_palette);
|
||||
break;
|
||||
|
||||
case Blitter::PALETTE_ANIMATION_NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
_cur_palette.count_dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Poll and handle a single event from the OS.
|
||||
@@ -615,15 +430,10 @@ void VideoDriver_Cocoa::InputLoop()
|
||||
}
|
||||
|
||||
/** Main game loop. */
|
||||
void VideoDriver_Cocoa::GameLoop()
|
||||
void VideoDriver_Cocoa::MainLoopReal()
|
||||
{
|
||||
for (;;) {
|
||||
@autoreleasepool {
|
||||
|
||||
InteractiveRandom(); // randomness
|
||||
|
||||
while (this->PollEvent()) {}
|
||||
|
||||
if (_exit_game) {
|
||||
/* Restore saved resolution if in fullscreen mode. */
|
||||
if (this->IsFullscreen()) _cur_resolution = this->orig_res;
|
||||
@@ -639,9 +449,16 @@ void VideoDriver_Cocoa::GameLoop()
|
||||
}
|
||||
|
||||
|
||||
/* Subclass of OTTD_CocoaView to fix Quartz rendering */
|
||||
@interface OTTD_QuartzView : NSView {
|
||||
VideoDriver_CocoaQuartz *driver;
|
||||
}
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_CocoaQuartz *)drv;
|
||||
@end
|
||||
|
||||
@implementation OTTD_QuartzView
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_Cocoa *)drv
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect andDriver:(VideoDriver_CocoaQuartz *)drv
|
||||
{
|
||||
if (self = [ super initWithFrame:frameRect ]) {
|
||||
self->driver = drv;
|
||||
@@ -690,4 +507,225 @@ void VideoDriver_Cocoa::GameLoop()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static FVideoDriver_CocoaQuartz iFVideoDriver_CocoaQuartz;
|
||||
|
||||
/** Clear buffer to opaque black. */
|
||||
static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height)
|
||||
{
|
||||
uint32 fill = Colour(0, 0, 0).data;
|
||||
for (uint32 y = 0; y < height; y++) {
|
||||
for (uint32 x = 0; x < pitch; x++) {
|
||||
buffer[y * pitch + x] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VideoDriver_CocoaQuartz::VideoDriver_CocoaQuartz()
|
||||
{
|
||||
this->window_width = 0;
|
||||
this->window_height = 0;
|
||||
this->window_pitch = 0;
|
||||
this->buffer_depth = 0;
|
||||
this->window_buffer = nullptr;
|
||||
this->pixel_buffer = nullptr;
|
||||
|
||||
this->cgcontext = nullptr;
|
||||
}
|
||||
|
||||
const char *VideoDriver_CocoaQuartz::Start(const StringList ¶m)
|
||||
{
|
||||
const char *err = this->Initialize();
|
||||
if (err != nullptr) return err;
|
||||
|
||||
int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
if (bpp != 8 && bpp != 32) {
|
||||
Stop();
|
||||
return "The cocoa quartz subdriver only supports 8 and 32 bpp.";
|
||||
}
|
||||
|
||||
bool fullscreen = _fullscreen;
|
||||
if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) {
|
||||
Stop();
|
||||
return "Could not create window";
|
||||
}
|
||||
|
||||
this->AllocateBackingStore(true);
|
||||
|
||||
if (fullscreen) this->ToggleFullscreen(fullscreen);
|
||||
|
||||
this->GameSizeChanged();
|
||||
this->UpdateVideoModes();
|
||||
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaQuartz::Stop()
|
||||
{
|
||||
this->VideoDriver_Cocoa::Stop();
|
||||
|
||||
CGContextRelease(this->cgcontext);
|
||||
|
||||
free(this->window_buffer);
|
||||
free(this->pixel_buffer);
|
||||
}
|
||||
|
||||
NSView *VideoDriver_CocoaQuartz::AllocateDrawView()
|
||||
{
|
||||
return [ [ OTTD_QuartzView alloc ] initWithFrame:[ this->cocoaview bounds ] andDriver:this ];
|
||||
}
|
||||
|
||||
/** Resize the window. */
|
||||
void VideoDriver_CocoaQuartz::AllocateBackingStore(bool force)
|
||||
{
|
||||
if (this->window == nil || this->cocoaview == nil || this->setup) return;
|
||||
|
||||
this->UpdatePalette(0, 256);
|
||||
|
||||
NSRect newframe = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
|
||||
|
||||
this->window_width = (int)newframe.size.width;
|
||||
this->window_height = (int)newframe.size.height;
|
||||
this->window_pitch = Align(this->window_width, 16 / sizeof(uint32)); // Quartz likes lines that are multiple of 16-byte.
|
||||
this->buffer_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
|
||||
/* Create Core Graphics Context */
|
||||
free(this->window_buffer);
|
||||
this->window_buffer = malloc(this->window_pitch * this->window_height * sizeof(uint32));
|
||||
/* Initialize with opaque black. */
|
||||
ClearWindowBuffer((uint32 *)this->window_buffer, this->window_pitch, this->window_height);
|
||||
|
||||
CGContextRelease(this->cgcontext);
|
||||
this->cgcontext = CGBitmapContextCreate(
|
||||
this->window_buffer, // data
|
||||
this->window_width, // width
|
||||
this->window_height, // height
|
||||
8, // bits per component
|
||||
this->window_pitch * 4, // bytes per row
|
||||
this->color_space, // color space
|
||||
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host
|
||||
);
|
||||
|
||||
assert(this->cgcontext != NULL);
|
||||
CGContextSetShouldAntialias(this->cgcontext, FALSE);
|
||||
CGContextSetAllowsAntialiasing(this->cgcontext, FALSE);
|
||||
CGContextSetInterpolationQuality(this->cgcontext, kCGInterpolationNone);
|
||||
|
||||
if (this->buffer_depth == 8) {
|
||||
free(this->pixel_buffer);
|
||||
this->pixel_buffer = malloc(this->window_width * this->window_height);
|
||||
if (this->pixel_buffer == nullptr) usererror("Out of memory allocating pixel buffer");
|
||||
} else {
|
||||
free(this->pixel_buffer);
|
||||
this->pixel_buffer = nullptr;
|
||||
}
|
||||
|
||||
/* Tell the game that the resolution has changed */
|
||||
_screen.width = this->window_width;
|
||||
_screen.height = this->window_height;
|
||||
_screen.pitch = this->buffer_depth == 8 ? this->window_width : this->window_pitch;
|
||||
_screen.dst_ptr = this->GetVideoPointer();
|
||||
|
||||
/* Redraw screen */
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
this->GameSizeChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode.
|
||||
*
|
||||
* @param left The x coord for the left edge of the box to blit.
|
||||
* @param top The y coord for the top edge of the box to blit.
|
||||
* @param right The x coord for the right edge of the box to blit.
|
||||
* @param bottom The y coord for the bottom edge of the box to blit.
|
||||
*/
|
||||
void VideoDriver_CocoaQuartz::BlitIndexedToView32(int left, int top, int right, int bottom)
|
||||
{
|
||||
const uint32 *pal = this->palette;
|
||||
const uint8 *src = (uint8*)this->pixel_buffer;
|
||||
uint32 *dst = (uint32*)this->window_buffer;
|
||||
uint width = this->window_width;
|
||||
uint pitch = this->window_pitch;
|
||||
|
||||
for (int y = top; y < bottom; y++) {
|
||||
for (int x = left; x < right; x++) {
|
||||
dst[y * pitch + x] = pal[src[y * width + x]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Update the palette */
|
||||
void VideoDriver_CocoaQuartz::UpdatePalette(uint first_color, uint num_colors)
|
||||
{
|
||||
if (this->buffer_depth != 8) return;
|
||||
|
||||
for (uint i = first_color; i < first_color + num_colors; i++) {
|
||||
uint32 clr = 0xff000000;
|
||||
clr |= (uint32)_cur_palette.palette[i].r << 16;
|
||||
clr |= (uint32)_cur_palette.palette[i].g << 8;
|
||||
clr |= (uint32)_cur_palette.palette[i].b;
|
||||
this->palette[i] = clr;
|
||||
}
|
||||
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
void VideoDriver_CocoaQuartz::CheckPaletteAnim()
|
||||
{
|
||||
if (_cur_palette.count_dirty != 0) {
|
||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||
|
||||
switch (blitter->UsePaletteAnimation()) {
|
||||
case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
|
||||
this->UpdatePalette(_cur_palette.first_dirty, _cur_palette.count_dirty);
|
||||
break;
|
||||
|
||||
case Blitter::PALETTE_ANIMATION_BLITTER:
|
||||
blitter->PaletteAnimate(_cur_palette);
|
||||
break;
|
||||
|
||||
case Blitter::PALETTE_ANIMATION_NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
_cur_palette.count_dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Draw window */
|
||||
void VideoDriver_CocoaQuartz::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
/* Check if we need to do anything */
|
||||
if (IsEmptyRect(this->dirty_rect) || [ this->window isMiniaturized ]) return;
|
||||
|
||||
/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
|
||||
if (this->buffer_depth == 8) {
|
||||
BlitIndexedToView32(
|
||||
this->dirty_rect.left,
|
||||
this->dirty_rect.top,
|
||||
this->dirty_rect.right,
|
||||
this->dirty_rect.bottom
|
||||
);
|
||||
}
|
||||
|
||||
NSRect dirtyrect;
|
||||
dirtyrect.origin.x = this->dirty_rect.left;
|
||||
dirtyrect.origin.y = this->window_height - this->dirty_rect.bottom;
|
||||
dirtyrect.size.width = this->dirty_rect.right - this->dirty_rect.left;
|
||||
dirtyrect.size.height = this->dirty_rect.bottom - this->dirty_rect.top;
|
||||
|
||||
/* Notify OS X that we have new content to show. */
|
||||
[ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
|
||||
|
||||
/* Tell the OS to get our contents to screen as soon as possible. */
|
||||
[ CATransaction flush ];
|
||||
|
||||
this->dirty_rect = {};
|
||||
}
|
||||
|
||||
#endif /* WITH_COCOA */
|
||||
|
||||
@@ -55,6 +55,8 @@ extern NSString *OTTDMainLaunchGameEngine;
|
||||
@end
|
||||
|
||||
|
||||
extern bool _allow_hidpi_window;
|
||||
|
||||
bool CocoaSetupApplication();
|
||||
void CocoaExitApplication();
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ static std::vector<WChar> NSStringToUTF32(NSString *s)
|
||||
[ e release ];
|
||||
|
||||
/* Hand off to main application code. */
|
||||
drv->GameLoop();
|
||||
drv->MainLoopReal();
|
||||
|
||||
/* We are done, thank you for playing. */
|
||||
[ self performSelectorOnMainThread:@selector(stopEngine) withObject:nil waitUntilDone:FALSE ];
|
||||
|
||||
Reference in New Issue
Block a user