18#if defined(WITH_COCOA) || defined(DOXYGEN_API)
20#include "../../stdafx.h"
21#include "../../os/macosx/macos.h"
23#include "../../os/macosx/macos_objective_c.h"
24#include "../../openttd.h"
25#include "../../debug.h"
26#include "../../error_func.h"
27#include "../../core/geometry_func.hpp"
28#include "../../core/math_func.hpp"
31#include "../../blitter/factory.hpp"
32#include "../../framerate_type.h"
33#include "../../gfx_func.h"
34#include "../../thread.h"
35#include "../../core/random_func.hpp"
36#include "../../progress.h"
37#include "../../settings_type.h"
38#include "../../window_func.h"
39#include "../../window_gui.h"
66VideoDriver_Cocoa::VideoDriver_Cocoa(
bool uses_hardware_acceleration)
70 this->buffer_locked =
false;
72 this->refresh_sys_sprites =
true;
75 this->cocoaview = nil;
78 this->colour_space =
nullptr;
80 this->dirty_rect = {};
127 Rect r = {left, top, left + width, top + height};
152 NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
153 w = std::min(w, (
int)screen_size.width);
154 h = std::min(h, (
int)screen_size.height);
156 NSRect contentRect = NSMakeRect(0, 0, w, h);
157 [ this->
window setContentSize:contentRect.size ];
160 float content_height = [ this->
window contentRectForFrameRect:[ this->
window frame ] ].size.height;
161 contentRect.size.height =
Clamp(h, 0, (
int)content_height);
164 h = (int)contentRect.size.height;
165 [ this->
cocoaview setFrameSize:contentRect.size ];
169 this->AllocateBackingStore();
183 if ([ this->
window respondsToSelector:
@selector(toggleFullScreen:) ]) {
184 [ this->
window performSelector:
@selector(toggleFullScreen:) withObject:this->
window ];
187 [ NSMenu setMenuBarVisible:!full_screen ];
205 [ this->
window refreshSystemSprites ];
216 this->AllocateBackingStore(
true);
225 [ [ this->
cocoaview inputContext ] performSelectorOnMainThread:
@selector(discardMarkedText) withObject:nil waitUntilDone:[ NSThread isMainThread ] ];
236 std::vector<int> rates{};
238 std::array<CGDirectDisplayID, 16> displays;
241 CGGetActiveDisplayList(displays.size(), displays.data(), &count);
243 for (uint32_t i = 0; i < count; i++) {
244 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
245 int rate =
static_cast<int>(CGDisplayModeGetRefreshRate(mode));
246 if (rate > 0) rates.push_back(rate);
247 CGDisplayModeRelease(mode);
259 NSRect frame = [ [ NSScreen mainScreen ] frame ];
260 return {
static_cast<uint
>(NSWidth(frame)),
static_cast<uint
>(NSHeight(frame)) };
273 assert(_screen.dst_ptr !=
nullptr);
281 if (_screen.dst_ptr !=
nullptr) {
284 _screen.dst_ptr =
nullptr;
296 return this->
window != nil && ([ this->
window styleMask ] & NSWindowStyleMaskFullScreen) != 0;
307 _fullscreen = fullscreen;
328 NSSize screen = [ [ this->
window screen ] frame ].size;
329 _resolutions.emplace_back((uint)screen.width, (uint)screen.height);
333 NSSize maxSize = [ [ NSScreen mainScreen] visibleFrame ].size;
335 if (d.width < maxSize.width && d.height < maxSize.height)
_resolutions.push_back(d);
337 _resolutions.emplace_back((uint)maxSize.width, (uint)maxSize.height);
352 NSSize screen_size = [ [ NSScreen mainScreen ] frame ].size;
353 if (width > screen_size.width) width = screen_size.width;
354 if (height > screen_size.height) height = screen_size.height;
356 NSRect contentRect = NSMakeRect(0, 0, width, height);
359 unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable;
360 this->
window = [ [
OTTD_CocoaWindow alloc ] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:NO driver:this ];
361 if (this->
window == nil) {
362 Debug(driver, 0,
"Could not create the Cocoa window.");
370 if ([ this->
window respondsToSelector:
@selector(toggleFullScreen:) ]) {
371 NSWindowCollectionBehavior behavior = [ this->
window collectionBehavior ];
372 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
373 [ this->
window setCollectionBehavior:behavior ];
375 NSButton *fullscreenButton = [ this->
window standardWindowButton:NSWindowZoomButton ];
376 [ fullscreenButton setAction:
@selector(toggleFullScreen:) ];
377 [ fullscreenButton setTarget:this->
window ];
384 [ this->
window makeKeyAndOrderFront:nil ];
387 NSRect view_frame = [ this->
window contentRectForFrameRect:[ this->
window frame ] ];
390 Debug(driver, 0,
"Could not create the event wrapper view.");
394 [ this->
cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
397 NSView *draw_view = this->AllocateDrawView();
398 if (draw_view == nil) {
399 Debug(driver, 0,
"Could not create the drawing view.");
403 [ draw_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ];
407 [ this->
cocoaview addSubview:draw_view ];
409 [ draw_view release ];
411 [ this->
window setColorSpace:[ NSColorSpace sRGBColorSpace ] ];
413 this->
colour_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
415 if (this->
colour_space ==
nullptr) FatalError(
"Could not get a valid colour space for drawing.");
429 NSEventMask mask = NSEventMaskAny;
430 NSEvent *
event = [ NSApp nextEventMatchingMask:mask untilDate:[ NSDate distantPast ] inMode:NSDefaultRunLoopMode dequeue:YES ];
432 if (event == nil)
return false;
434 [ NSApp sendEvent:event ];
441 NSUInteger cur_mods = [ NSEvent modifierFlags ];
492 if (self = [ super initWithFrame:frameRect ]) {
496 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
497 self.wantsLayer = YES;
499 self.layer.magnificationFilter = kCAFilterNearest;
500 self.layer.opaque = YES;
539 self.layer.contents = (__bridge id)fullImage;
540 CGImageRelease(fullImage);
546 [
super viewDidChangeBackingProperties ];
548 self.layer.contentsScale = [ driver->cocoaview getContentsScale ];
564 uint32_t fill = Colour(0, 0, 0).data;
565 for (uint32_t y = 0; y < height; y++) {
566 for (uint32_t x = 0; x < pitch; x++) {
567 buffer[y * pitch + x] = fill;
572VideoDriver_CocoaQuartz::VideoDriver_CocoaQuartz()
576 this->window_pitch = 0;
590 if (bpp != 8 && bpp != 32) {
592 return "The cocoa quartz subdriver only supports 8 and 32 bpp.";
595 bool fullscreen = _fullscreen;
598 return "Could not create window";
624NSView *VideoDriver_CocoaQuartz::AllocateDrawView()
626 return [ [
OTTD_QuartzView alloc ] initWithFrame:[ this->cocoaview bounds ] andDriver:this ];
639 NSRect newframe = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
657 this->window_pitch * 4,
659 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host
663 CGContextSetShouldAntialias(this->
cgcontext, FALSE);
664 CGContextSetAllowsAntialiasing(this->
cgcontext, FALSE);
665 CGContextSetInterpolationQuality(this->
cgcontext, kCGInterpolationNone);
680 this->
MakeDirty(0, 0, _screen.width, _screen.height);
694 const uint32_t *pal = this->
palette;
698 uint pitch = this->window_pitch;
700 for (
int y = top; y < bottom; y++) {
701 for (
int x = left; x < right; x++) {
702 dst[y * pitch + x] = pal[src[y * width + x]];
716 for (uint i = first_colour; i < first_colour + num_colours; i++) {
717 uint32_t clr = 0xff000000;
724 this->
MakeDirty(0, 0, _screen.width, _screen.height);
762 this->dirty_rect.top,
763 this->dirty_rect.right,
764 this->dirty_rect.bottom
775 [ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
778 [ CATransaction flush ];
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
How all blitters should look like.
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
@ None
No palette animation.
@ Blitter
The blitter takes care of the palette animation.
@ VideoBackend
Palette animation should be done by video backend (8bpp only!)
virtual void PostResize()
Post resize event.
std::optional< std::string_view > Start(const StringList ¶m) override
Start this driver.
CGContextRef cgcontext
Context reference for Quartz subdriver.
uint32_t palette[256]
Colour Palette.
int buffer_depth
Colour depth of used frame buffer.
int window_width
Current window width in pixel.
void CheckPaletteAnim() override
Process any pending palette animation.
std::unique_ptr< uint32_t[]> window_buffer
Colour translation from palette to screen.
void AllocateBackingStore(bool force=false) override
Resize the window.
void BlitIndexedToView32(int left, int top, int right, int bottom)
This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode.
std::unique_ptr< uint8_t[]> pixel_buffer
used for direct pixel access
void UpdatePalette(uint first_colour, uint num_colours)
Update the palette.
void Stop() override
Stop Cocoa video driver.
void Paint() override
Draw window.
void * GetVideoPointer() override
Get a pointer to the video buffer.
int window_height
Current window height in pixel.
void GameSizeChanged()
Handle a change of the display area.
bool ToggleFullscreen(bool fullscreen) override
Toggle between windowed and full screen mode for cocoa display driver.
void UpdateVideoModes()
Update the video mode.
std::vector< int > GetListOfMonitorRefreshRates() override
Get refresh rates of all connected monitors.
Dimension GetScreenSize() const override
Get the resolution of the main screen.
std::optional< std::string_view > Initialize()
Common driver initialization.
bool refresh_sys_sprites
System sprites need refreshing.
void MainLoop() override
Start the main programme loop when using a cocoa video driver.
OTTD_CocoaWindow * window
Pointer to window object.
OTTD_CocoaView * cocoaview
Pointer to view object.
virtual void ReleaseVideoPointer()
Hand video buffer back to the drawing backend.
virtual void * GetVideoPointer()=0
Get a pointer to the video buffer.
OTTD_CocoaWindowDelegate * delegate
Window delegate object.
bool IsFullscreen()
Are we in fullscreen mode?
void InputLoop() override
Handle input logic, is CTRL pressed, should we fast-forward, etc.
bool MakeWindow(int width, int height)
Build window and view with a given size.
bool PollEvent() override
Poll and handle a single event from the OS.
void MakeDirty(int left, int top, int width, int height) override
Set dirty a rectangle managed by a cocoa video subdriver.
void PopulateSystemSprites() override
Populate all sprites in cache.
bool LockVideoBuffer() override
Lock video buffer for drawing if it isn't already mapped.
void EditBoxLostFocus() override
An edit box lost the input focus.
void ClearSystemSprites() override
Clear all cached sprites.
Rect dirty_rect
Region of the screen that needs redrawing.
void UnlockVideoBuffer() override
Unlock video buffer.
void Stop() override
Stop Cocoa video driver.
bool buffer_locked
Video buffer was locked by the main thread.
Dimension orig_res
Saved window size for non-fullscreen mode.
bool setup
Window is currently being created.
bool ChangeResolution(int w, int h) override
Change the resolution when using a cocoa video driver.
bool AfterBlitterChange() override
Callback invoked after the blitter was changed.
CGColorSpaceRef colour_space
Window colour space.
void MainLoopReal()
Main game loop.
The base of all video drivers.
bool fast_forward_key_pressed
The fast-forward key is being pressed.
void Tick()
Give the video-driver a tick.
void SleepTillNextTick()
Sleep till the next tick is about to happen.
void StartGameThread()
Start the loop for game-tick.
void StopGameThread()
Stop the loop for the game-tick.
void UpdateAutoResolution()
Apply resolution auto-detection and clamp to sensible defaults.
static Palette _local_palette
Current palette to use for drawing.
bool _cocoa_video_started
Is the Cocoa video driver running.
static FVideoDriver_CocoaQuartz iFVideoDriver_CocoaQuartz
Register the cocoa video driver.
static Palette _local_palette
Current palette to use for drawing.
bool _cocoa_video_started
Is the Cocoa video driver running.
static const Dimension _default_resolutions[]
List of common display/window sizes.
static void ClearWindowBuffer(uint32_t *buffer, uint32_t pitch, uint32_t height)
Clear buffer to opaque black.
bool _tab_is_down
Is tab button pressed.
OS interface for the cocoa video driver.
bool CocoaSetupApplication()
Startup the application.
void CocoaExitApplication()
Deregister app delegate.
NSString * OTTDMainLaunchGameEngine
Name of notification observer used to restart the game loop if necessary.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
bool GetDriverParamBool(const StringList &parm, std::string_view name)
Get a boolean parameter the list of parameters.
std::vector< Dimension > _resolutions
List of resolutions.
Dimension _cur_resolution
The current resolution.
@ PFE_VIDEO
Speed of painting drawn video buffer.
Rect BoundingRect(const Rect &r1, const Rect &r2)
Compute the bounding rectangle around two rectangles.
bool IsEmptyRect(const Rect &r)
Check if a rectangle is empty.
bool _shift_pressed
Is Shift pressed?
bool _ctrl_pressed
Is Ctrl pressed?
void HandleCtrlChanged()
State of CONTROL key has changed.
void HandleTextInput(std::string_view str, bool marked=false, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Handle text input.
Subclass of NSView to support mouse awareness and text input.
Delegate for our NSWindow to send ask for quit on close.
Subclass of NSWindow to cater our special needs.
Subclass of OTTD_CocoaView to fix Quartz rendering.
BOOL isOpaque()
Specifies whether the view is opaque.
void updateLayer()
Updates the layer based on driver data.
BOOL wantsUpdateLayer()
Specifies whether the view wants updates for layer.
BOOL acceptsFirstResponder()
Specifies whether the view accepts first responder.
VideoDriver_CocoaQuartz * driver
The driver to fix rendering for.
void viewDidChangeBackingProperties()
Updates members with new values after changes in driver.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
bool CopyPalette(Palette &local_palette, bool force_copy)
Copy the current palette if the palette was updated.
ClientSettings _settings_client
The current settings for this game.
std::vector< std::string > StringList
Type for a list of strings.
GUISettings gui
settings related to the GUI
Dimensions (a width and height) of a rectangle in 2D.
uint8_t right_mouse_btn_emulation
should we emulate right mouse clicking?
Information about the currently used palette.
int first_dirty
The first dirty element.
int count_dirty
The number of dirty elements.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Specification of a rectangle with absolute coordinates of all edges.
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
@ WC_GAME_OPTIONS
Game options window; Window numbers: