summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2021-02-07 20:50:41 +0100
committerMichael Lutz <michi@icosahedron.de>2021-02-22 22:16:07 +0100
commit2f25e9bdf8c8c2952ba7f8adb9bd73302fef37c2 (patch)
tree76d7216947a38a2dbea46b35a400a356a43970ed
parent421b599541069318817bd0e0768984a662a84753 (diff)
downloadopenttd-2f25e9bdf8c8c2952ba7f8adb9bd73302fef37c2.tar.xz
Codechange: [OSX] Separate video driver into a base and a Quartz implementation.
-rw-r--r--src/video/cocoa/cocoa_v.h71
-rw-r--r--src/video/cocoa/cocoa_v.mm442
2 files changed, 275 insertions, 238 deletions
diff --git a/src/video/cocoa/cocoa_v.h b/src/video/cocoa/cocoa_v.h
index f9f678157..4d683d622 100644
--- a/src/video/cocoa/cocoa_v.h
+++ b/src/video/cocoa/cocoa_v.h
@@ -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 &param) override;
void Stop() override;
void MainLoop() override;
@@ -61,40 +47,73 @@ 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 AllocateBackingStore();
+ virtual void AllocateBackingStore() = 0;
protected:
+ Rect dirty_rect; ///< Region of the screen that needs redrawing.
Dimension GetScreenSize() const override;
float GetDPIScale() override;
void InputLoop() override;
- void Paint() override;
- void CheckPaletteAnim() override;
+
+ void GameSizeChanged();
+
+ const char *Initialize();
+
+ void UpdateVideoModes();
+
+ bool MakeWindow(int width, int height);
+
+ virtual NSView* AllocateDrawView() = 0;
private:
bool PollEvent();
bool IsFullscreen();
- void GameSizeChanged();
+};
- void UpdateVideoModes();
+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
- bool MakeWindow(int width, int height);
+ int window_width; ///< Current window width in pixel
+ int window_height; ///< Current window height in pixel
+ int window_pitch;
- void UpdatePalette(uint first_color, uint num_colors);
+ uint32 palette[256]; ///< Colour Palette
void BlitIndexedToView32(int left, int top, int right, int bottom);
+ void UpdatePalette(uint first_color, uint num_colors);
+
+public:
+ CGContextRef cgcontext; ///< Context reference for Quartz subdriver
+
+ VideoDriver_CocoaQuartz();
+
+ const char *Start(const StringList &param) override;
+ void Stop() override;
+
+ /** Return driver name */
+ const char *GetName() const override { return "cocoa"; }
+
+ void AllocateBackingStore() override;
+
+protected:
+ void Paint() override;
+ void CheckPaletteAnim() override;
+
+ NSView* AllocateDrawView() override;
};
-class FVideoDriver_Cocoa : public DriverFactoryBase {
+class FVideoDriver_CocoaQuartz : public DriverFactoryBase {
public:
- FVideoDriver_Cocoa() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
- Driver *CreateInstance() const override { return new VideoDriver_Cocoa(); }
+ FVideoDriver_CocoaQuartz() : DriverFactoryBase(Driver::DT_VIDEO, 10, "cocoa", "Cocoa Video Driver") {}
+ Driver *CreateInstance() const override { return new VideoDriver_CocoaQuartz(); }
};
#endif /* VIDEO_COCOA_H */
diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm
index 7baed796f..36ad0cafb 100644
--- a/src/video/cocoa/cocoa_v.mm
+++ b/src/video/cocoa/cocoa_v.mm
@@ -81,25 +81,9 @@ 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->window = nil;
@@ -107,7 +91,6 @@ VideoDriver_Cocoa::VideoDriver_Cocoa()
this->delegate = nil;
this->color_space = nullptr;
- this->cgcontext = nullptr;
this->dirty_rect = {};
}
@@ -124,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.";
@@ -147,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;
}
@@ -217,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();
@@ -251,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;
}
@@ -294,12 +252,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;
@@ -392,7 +344,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;
@@ -414,171 +366,11 @@ 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.
@@ -642,9 +434,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;
@@ -693,4 +492,223 @@ 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 &param)
+{
+ 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";
+ }
+
+ 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()
+{
+ 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->buffer_depth == 8 ? this->pixel_buffer : this->window_buffer;
+
+ /* 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 */