瀏覽代碼

CI: Fix build errors with Xcode 14.3 and platform SDK 13.3

Xcode 14.3 and the macOS 13.3 platform SDK introduced a few breaking
changes:

* Updated AppleClang emits warnings about unqualified std cast calls
  when using C++ - as `move` is too broad a word, developers are to use
  `std::move` to make this explicit. Alas this is exactly what `json11`
  uses and because that library is archived, there is no possibility
  of an upstream update.

* Apple guarded calls to old screen capture APIs as "available but
  deprecated", but seems to have chosen the wrong lower version
  boundary: The calls are flagged as being available for macOS 13 and
  macOS 14 only.

  To fix this, the existing macOS platform SDK header is replaced by a
  local copy that uses macOS 11 as the lower boundary (the oldest macOS
  version supported by obs-studio anyway)
PatTheMav 2 年之前
父節點
當前提交
ee3c2d0e8a

+ 4 - 0
UI/cmake/os-macos.cmake

@@ -15,3 +15,7 @@ target_link_libraries(obs-studio PRIVATE ${APPKIT} ${AVFOUNDATION} ${APPLICATION
 
 target_compile_options(obs-studio PRIVATE -Wno-error=float-conversion -Wno-error=implicit-int-conversion
                                           -Wno-error=shorten-64-to-32)
+
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)
+  target_compile_options(obs-studio PRIVATE -Wno-error=unqualified-std-cast-call)
+endif()

+ 7 - 26
UI/platform-osx.mm

@@ -308,34 +308,15 @@ MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type,
 		break;
 	}
 	case kScreenCapture: {
-#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000
-		if (@available(macOS 11.0, *)) {
-			permissionResponse = (CGPreflightScreenCaptureAccess()
+		permissionResponse = (CGPreflightScreenCaptureAccess()
+					      ? kPermissionAuthorized
+					      : kPermissionDenied);
+
+		if (permissionResponse != kPermissionAuthorized &&
+		    prompt_for_permission) {
+			permissionResponse = (CGRequestScreenCaptureAccess()
 						      ? kPermissionAuthorized
 						      : kPermissionDenied);
-
-			if (permissionResponse != kPermissionAuthorized &&
-			    prompt_for_permission) {
-				permissionResponse =
-					(CGRequestScreenCaptureAccess()
-						 ? kPermissionAuthorized
-						 : kPermissionDenied);
-			}
-
-		} else {
-#else
-		{
-#endif
-			CGDisplayStreamRef stream = CGDisplayStreamCreate(
-				CGMainDisplayID(), 1, 1,
-				kCVPixelFormatType_32BGRA, nil, nil);
-
-			if (stream) {
-				permissionResponse = kPermissionAuthorized;
-				CFRelease(stream);
-			} else {
-				permissionResponse = kPermissionDenied;
-			}
 		}
 
 		blog(LOG_INFO, "[macOS] Permission for screen capture %s.",

+ 352 - 0
plugins/mac-capture/CGDisplayStream.h

@@ -0,0 +1,352 @@
+/* CoreGraphics - CGDisplayStream.h
+   Copyright (c) 2011-2013 Apple Inc.
+   All rights reserved. */
+
+#ifndef CGDISPLAYSTREAM_H_
+#define CGDISPLAYSTREAM_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFAvailability.h>
+#include <stdint.h>
+#include <dispatch/dispatch.h>
+
+#include <CoreGraphics/CGDirectDisplay.h>
+
+CF_IMPLICIT_BRIDGING_ENABLED
+
+CF_ASSUME_NONNULL_BEGIN
+
+#ifdef __BLOCKS__
+
+/*!
+ @typedef CGDisplayStreamRef
+ @abstract An opaque reference to a CGDisplayStream object
+ @discussion A CGDisplayStream provides a streaming API for capturing display updates in a realtime manner.  It can also provide
+ scaling and color space conversion services, as well as allow capturing sub regions of the display.   Callbacks can be targetted
+ at either a traditional CFRunLoop, or at a dispatch queue.
+*/
+typedef struct CF_BRIDGED_TYPE(id) CGDisplayStream *CGDisplayStreamRef;
+
+/*!
+ @typedef CGDisplayStreamUpdateRef
+ @abstract An opaque reference to a single frame's extra metadata that describes useful frame delta information
+ @discussion A CGDisplayStreamUpdate encapsulates information about what portions of a frame have changed relative to
+ a previously delivered frame.   This includes regions that were changed in any way, which ones were actually redrawn, and which
+ regions were merely copied from one place to another.   A routine is provided to merge two update refs together in cases
+ where apps need to coalesce the values because they decided to skip processing for one or more frames.
+*/
+typedef const struct CF_BRIDGED_TYPE(id)
+	CGDisplayStreamUpdate *CGDisplayStreamUpdateRef;
+
+/*!
+ @enum CGDisplayStreamUpdateRectType
+ @abstract Used to select which array of rectangles to be returned by CGDisplayUpdateGetRects
+ @const kCGDisplayStreamUpdateRefreshedRects The rectangles that were refreshed on the display, not counting moved rectangles
+ @const kCGDisplayStreamUpdateMovedRects The rectangles that were simply moved from one part of the display to another
+ @const kCGDisplayStreamUpdateDirtyRects The union of both refreshed and moved rects
+ @const kCGDisplayStreamUpdateReducedDirtyRects A possibly simplified (but overstated) array of dirty rectangles
+*/
+typedef CF_ENUM(int32_t, CGDisplayStreamUpdateRectType) {
+	kCGDisplayStreamUpdateRefreshedRects,
+	kCGDisplayStreamUpdateMovedRects,
+	kCGDisplayStreamUpdateDirtyRects,
+	kCGDisplayStreamUpdateReducedDirtyRects,
+};
+
+/*!
+ @enum CGDisplayStreamFrameStatus
+ @abstract Provides information about incoming frame updates
+ @const kCGDisplayStreamFrameStatusFrameComplete A new frame has been generated by the Window Server for a particular display at time displayTime
+ @const kCGDisplayStreamFrameStatusFrameIdle The Window Server did not generate a new frame for displayTime
+ @const kCGDisplayStreamFrameStatusFrameBlank As of displayTime, the display has gone blank
+ @const kCGDisplayStreamFrameStatusStopped The display stream has stopped and no more calls will be made to the handler until the stream is started.
+*/
+typedef CF_ENUM(int32_t, CGDisplayStreamFrameStatus) {
+	kCGDisplayStreamFrameStatusFrameComplete,
+	kCGDisplayStreamFrameStatusFrameIdle,
+	kCGDisplayStreamFrameStatusFrameBlank,
+	kCGDisplayStreamFrameStatusStopped,
+};
+
+/*
+ @callback CGDisplayStreamFrameAvailableHandler
+ @abstract The block prototype used for new frame delivery by CGDisplayStream objects
+ @discussion For each frame that is generated by the WindowServer for a particular display, the user provided block is invoked and provides the user with an IOSurfaceRef
+ that contains the pixel data for the new frame, as well as a CGDisplayStreamUpdateRef that contains all of the metadata associated with that IOSurface.
+ @param frameSurface The IOSurfaceRef for the current frame.  May be NULL in some cases.   If you intend to hold on to the IOSurface beyond the lifetime of
+ the handler call, you must CFRetain() the IOSurface until you are done with it *and* you must call IOSurfaceIncrementUseCount() to let the CGDisplayStream know
+ that the frame is not ready for re-use.  Once you are finished using the IOSurfaceRef you must then call IOSurfaceDecrementUseCount().  If you are maintaing
+ any kind of external cache of information about the IOSurface (such as a GL texture object), you must keep a CFRetain() on the IOSurface to prevent it from going
+ away until you remove it from your cache.   You can not depend on the set of IOSurfaces being used by the display stream as being static, so you should implement
+ some kind of age-out behavior for your cache for IOSurfaces that have not been re-used in a while.
+ @param displayTime The mach_absolute_time() of when the corresponding frame was to be displayed by the WindowServer
+ @param updateRef The CGDisplayStreamUpdateRef for the current frame.   Note: If you want to keep the CGDisplayStreamUpdateRef around beyond the lifetime
+ of the handler, you must CFRetain() it, as it will be CFRelease()'d by the CGDisplayStream after the handler returns.   The updateRef will be NULL in cases
+ when status is not kCGDisplayStreamFrameStatusFrameComplete.
+ */
+typedef void (^CGDisplayStreamFrameAvailableHandler)(
+	CGDisplayStreamFrameStatus status, uint64_t displayTime,
+	IOSurfaceRef __nullable frameSurface,
+	CGDisplayStreamUpdateRef __nullable updateRef);
+
+/*!
+ @function CGDisplayStreamUpdateGetTypeID
+ @abstract Returns the CF "class" ID for CGDisplayStreamUpdate
+ @result The CFTypeID of the CGDisplayStreamUpdate class.
+*/
+CG_EXTERN CFTypeID
+CGDisplayStreamUpdateGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamOutputType instead");
+
+/*!
+ @function CGDisplayStreamUpdateGetRects
+ @abstract Returns a pointer to an array of CGRect structs that describe what parts of the frame have changed relative
+ to the previously delivered frame.   This rectangle list encapsulates both the update rectangles and movement rectangles.
+ @param updateRef The CGDisplayStreamUpdateRef
+ @param rectCount A pointer to where the count of the number of rectangles in the array is to be returned. Must not be NULL.
+ @result A pointer to the array of CGRectangles.  This array should not be freed by the caller.
+*/
+CG_EXTERN const CGRect *__nullable CGDisplayStreamUpdateGetRects(
+	CGDisplayStreamUpdateRef __nullable updateRef,
+	CGDisplayStreamUpdateRectType rectType, size_t *rectCount)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
+
+/*!
+ @function CGDisplayStreamUpdateCreateMerged
+ @abstract Merge two CGDisplayUpdateRefs into a new one.
+ @discussion In cases where the client wishes to drop certain frame updates, this function may be used to merge two
+ CGDisplayUpdateRefs together.  The core bit of functionality here is generating a new set of refresh/move/dirty
+ rectangle arrays that properly represent the union of the deltas between the two frames.  Note that the ordering of
+ the two refs is important.
+
+ @param firstUpdate The first update (in a temporal sense)
+ @param secondUpdate The second update (in a temporal sense)
+ @result The new CGDisplayStreamUpdateRef
+*/
+CG_EXTERN CGDisplayStreamUpdateRef __nullable
+CGDisplayStreamUpdateCreateMergedUpdate(
+	CGDisplayStreamUpdateRef __nullable firstUpdate,
+	CGDisplayStreamUpdateRef __nullable secondUpdate)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
+
+/*!
+ @function CGDisplayStreamUpdateGetMovedRectsDelta
+ @abstract Return the movement dx and dy values for a single update
+ @param updateRef The CGDisplayStreamUpdateRef
+ @param dx A pointer to a CGFloat to store the x component of the movement delta
+ @param dy A pointer to a CGFloat to store the y component of the movement delta
+ @discussion The delta values describe the offset from the moved rectangles back to the source location.
+*/
+CG_EXTERN void CGDisplayStreamUpdateGetMovedRectsDelta(
+	CGDisplayStreamUpdateRef __nullable updateRef, CGFloat *dx, CGFloat *dy)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's SCStreamFrameInfo with SCStreamFrameInfoContentRect instead");
+
+/*!
+ @function CGDisplayStreamGetDropCount
+ @abstract Return how many frames (if any) have been dropped since the last call to the handler.
+ @param updateRef The CGDisplayStreamUpdateRef
+ @result The number of dropped frames
+ @discussion This call is primarily useful for performance measurement to determine if the client is keeping up with
+ all WindowServer updates.
+*/
+CG_EXTERN size_t
+CGDisplayStreamUpdateGetDropCount(CGDisplayStreamUpdateRef __nullable updateRef)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStreamFrameInfo to replace CGDisplayStreamUpdate");
+
+/* Optional CGDisplayStream Properties */
+
+/*!
+ @const kCGDisplayStreamSourceRect
+ @discussion This may be used to request a subregion of the display to be provided as the source of the display stream.  Use
+ CGRectCreateDictionaryRepresentation to convert from a CGRect to the value used here.   Note: The coordinate system for the
+ source rectangle is specified in display logical coordinates and not in pixels, in order to match the normal convention on
+ HiDPI displays.
+*/
+CG_EXTERN const CFStringRef kCGDisplayStreamSourceRect CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration sourceRect property instead"); /* Source rectangle to capture - defaults to entire display */
+
+/*!
+ @const kCGDisplayStreamDestinationRect
+ @discussion This may be used to request where within the destination buffer the display updates should be placed. Use
+ CGRectCreateDictionaryRepresentation to convert from a CGRect to the value used here.   Note: The coordinate system for
+ the destination rectangle is always specified in output pixels to match the fact that the output buffer size is also
+ specified in terms of pixels.
+ */
+CG_EXTERN const CFStringRef kCGDisplayStreamDestinationRect CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration destinationRect property instead"); /* Destination rectangle - defaults to entire buffer */
+
+/*!
+ @const kCGDisplayStreamPreserveAspectRatio
+ @discussion Enable/disable the work the Window Server will do to preserve the display aspect ratio.  By default the Window Server will
+ assume that it should preserve the original aspect ratio of the source display rect.  If the aspect ratio of the source display and
+ the display stream destination rect are not the same, black borders will be inserted at the top/bottom or right/left sides of the destination
+ in order to preserve the source aspect ratio.
+ */
+CG_EXTERN const CFStringRef kCGDisplayStreamPreserveAspectRatio CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration preserveAspectRatio property instead"); /* CFBoolean - defaults to true */
+
+/*!
+ @const kCGDisplayStreamColorSpace
+ @discussion Set the desired CGColorSpace of the output frames.  By default the color space will be that of the display.
+*/
+CG_EXTERN const CFStringRef kCGDisplayStreamColorSpace CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration colorSpaceName property instead"); /* Desired output color space (CGColorSpaceRef) - defaults to display color space */
+
+/*!
+ @const kCGDisplayStreamMinimumFrameTime
+ @discussion Request that the delta between frame updates be at least as much specified by this value.
+*/
+CG_EXTERN const CFStringRef kCGDisplayStreamMinimumFrameTime CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration minimumFrameInterval property instead"); /* CFNumber in seconds, defaults to zero. */
+
+/*!
+ @const kCGDisplayStreamShowCursor
+ @discussion Controls whether the cursor is embedded within the provided buffers or not.
+*/
+CG_EXTERN const CFStringRef kCGDisplayStreamShowCursor CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration showsCursor property instead"); /* CFBoolean - defaults to false */
+
+/*!
+ @const kCGDisplayStreamQueueDepth
+ @discussion Controls how many frames deep the frame queue will be.  Defaults to N.
+ */
+CG_EXTERN const CFStringRef kCGDisplayStreamQueueDepth CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration queueDepth property instead"); /* Queue depth in frames.  Defaults to 3. */
+
+/*!
+ @const kCGDisplayStreamYCbCrMatrix
+ @discussion When outputting frames in 420v or 420f format, this key may be used to control which YCbCr matrix is used
+ The value should be one of the three kCGDisplayStreamYCbCrMatrix values specified below.
+*/
+CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"Please use ScreenCaptureKit API's SCStreamConfiguration colorMatrix property");
+
+/* Supported YCbCr matrices. Note that these strings have identical values to the equivalent CoreVideo strings. */
+CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix_ITU_R_709_2;
+CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix_ITU_R_601_4;
+CG_EXTERN const CFStringRef kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995;
+
+/*!
+ @function CGDisplayStreamGetTypeID
+ @abstract Returns the CF "class" ID for CGDisplayStream
+ @result The CFTypeID of the CGDisplayStream class.
+*/
+CG_EXTERN CFTypeID CGDisplayStreamGetTypeID(void) CG_AVAILABLE_BUT_DEPRECATED(
+	11.0, 14.0,
+	"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
+
+/*!
+ @function CGDisplayStreamCreate
+ @abstract Creates a new CGDisplayStream intended to be used with a CFRunLoop
+ @discussion This function creates a new CGDisplayStream that is to be used to get a stream of frame updates
+ from a particular display.
+ @param display The CGDirectDisplayID to use as the source for generated frames
+ @param outputWidth The output width (in pixels, not points) of the frames to be generated.  Must not be zero.
+ @param outputHeight The output height (in pixels, not points) of the frames to be generated.  Must not be zero.
+ @param pixelFormat The desired CoreVideo/CoreMedia-style pixel format of the output IOSurfaces.  The currently
+ supported values are:
+
+ 'BGRA' Packed Little Endian ARGB8888
+ 'l10r' Packed Little Endian ARGB2101010
+ '420v' 2-plane "video" range YCbCr 4:2:0
+ '420f' 2-plane "full" range YCbCr 4:2:0
+
+ @param properties Any optional properties of the CGDisplayStream
+ @param handler A block that will be called for frame deliver.
+ @result The new CGDisplayStream object.
+*/
+CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreate(
+	CGDirectDisplayID display, size_t outputWidth, size_t outputHeight,
+	int32_t pixelFormat, CFDictionaryRef __nullable properties,
+	CGDisplayStreamFrameAvailableHandler __nullable handler)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
+
+/*!
+ @function CGDisplayStreamCreateWithDispatchQueue
+ @abstract Creates a new CGDisplayStream intended to be serviced by a block handler
+ @discussion This function creates a new CGDisplayStream that is to be used to get a stream of frame updates
+ from a particular display.
+ @param display The CGDirectDisplayID to use as the source for generated frames
+ @param outputWidth The output width (in pixels, not points) of the frames to be generated.  Must not be zero.
+ @param outputHeight The output height (in pixels, not points) of the frames to be generated.  Must not be zero.
+ @param pixelFormat The desired CoreVideo/CoreMedia-style pixel format of the output IOSurfaces
+ @param properties Any optional properties of the CGDisplayStream
+ @param queue The dispatch_queue_t that will be used to invoke the callback handler.
+ @param handler A block that will be called for frame deliver.
+ @result The new CGDisplayStream object.
+*/
+CG_EXTERN CGDisplayStreamRef __nullable CGDisplayStreamCreateWithDispatchQueue(
+	CGDirectDisplayID display, size_t outputWidth, size_t outputHeight,
+	int32_t pixelFormat, CFDictionaryRef __nullable properties,
+	dispatch_queue_t queue,
+	CGDisplayStreamFrameAvailableHandler __nullable handler)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's initWithFilter:configuration:delegate: instead");
+
+/*!
+ @function CGDisplayStreamStart
+ @abstract Begin delivering frame updates to the handler block.
+ @param displayStream to be started
+ @result kCGErrorSuccess If the display stream was started, otherwise an error.
+*/
+CG_EXTERN CGError
+CGDisplayStreamStart(CGDisplayStreamRef cg_nullable displayStream)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's startCaptureWithCompletionHandler: to start a stream instead");
+
+/*!
+ @function CGDisplayStreamStop
+ @abstract End delivery of frame updates to the handler block.
+ @param displayStream to be stopped
+ @result kCGErrorSuccess If the display stream was stopped, otherwise an error.
+ @discussion After this call returns, the CGDisplayStream callback function will eventually be called with a
+ status of kCGDisplayStreamFrameStatusStopped.  After that point it is safe to release the CGDisplayStream.
+ It is safe to call this function from within the handler block, but the previous caveat still applies.
+*/
+CG_EXTERN CGError
+CGDisplayStreamStop(CGDisplayStreamRef cg_nullable displayStream)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"Please use ScreenCaptureKit API's stopCaptureWithCompletionHandler: to stop a stream instead");
+
+/*!
+ @function CGDisplayStreamGetRunLoopSource
+ @abstract Return the singleton CFRunLoopSourceRef for a CGDisplayStream.
+ @param displayStream The CGDisplayStream object
+ @result The CFRunLoopSource for this displayStream.  Note: This function will return NULL if the
+ display stream was created via  CGDisplayStreamCreateWithDispatchQueue().
+*/
+CG_EXTERN CFRunLoopSourceRef __nullable
+CGDisplayStreamGetRunLoopSource(CGDisplayStreamRef cg_nullable displayStream)
+	CG_AVAILABLE_BUT_DEPRECATED(
+		11.0, 14.0,
+		"There is no direct replacement for this function. Please use ScreenCaptureKit API's SCStream to replace CGDisplayStream");
+
+#endif /* __BLOCKS__ */
+
+CF_ASSUME_NONNULL_END
+
+CF_IMPLICIT_BRIDGING_DISABLED
+
+#endif /* CGDISPLAYSTREAM_H_ */

+ 5 - 3
plugins/mac-capture/CMakeLists.txt

@@ -16,15 +16,17 @@ add_library(OBS::capture ALIAS mac-capture)
 
 target_sources(
   mac-capture
-  PRIVATE plugin-main.c
+  PRIVATE # cmake-format: sortable
           audio-device-enum.c
           audio-device-enum.h
+          CGDisplayStream.h
           mac-audio.c
           mac-display-capture.m
           mac-screen-capture.m
           mac-window-capture.m
-          window-utils.m
-          window-utils.h)
+          plugin-main.c
+          window-utils.h
+          window-utils.m)
 
 target_link_libraries(mac-capture PRIVATE OBS::libobs ${COREAUDIO} ${AUDIOUNIT} ${COREFOUNDATION} ${IOSURF} ${COCOA})
 

+ 1 - 1
plugins/mac-capture/mac-display-capture.m

@@ -4,7 +4,7 @@
 #include <pthread.h>
 
 #import <AvailabilityMacros.h>
-#import <CoreGraphics/CGDisplayStream.h>
+#import "CGDisplayStream.h"
 #import <Cocoa/Cocoa.h>
 
 #include "window-utils.h"