From a164f6cbd92f6774cfb5dd49ee94396c5df31ea8 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 30 Jul 2026 09:33:57 +0000 Subject: [PATCH] highgui: make Cocoa backend build with GCC and pre-10.7 SDKs - Wrap autorelease pools in CV_AUTORELEASE_POOL_BEGIN/END: clang keeps @autoreleasepool, GCC (whose Objective-C frontend lacks the syntax and ARC) gets an equivalent explicit NSAutoreleasePool. Restructure the few functions that returned from inside the pool scope. - Declare explicit ivars for CVView and synthesize all its properties: required by GCC and by the 32-bit (fragile) Objective-C ABI. - Use numeric availability values (1060/1070): MAC_OS_X_VERSION_10_7 is not defined by the 10.6 SDK and an undefined macro evaluates to 0 in #if, silently selecting the wrong branch. - Guard 10.7+ APIs (NSFullScreenWindowMask, toggleFullScreen:, convertRectToScreen:/FromScreen:, scrollingDelta*) and invoke convertSizeFromBacking: through a typed IMP so the NSSize struct-return ABI is correct when the method is not declared by the SDK. - Use pre-10.12 event constant names (NSScrollWheel) consistently with the rest of the file; message sends instead of dot syntax on old-SDK methods; CGFloat casts for 32-bit std::min/max; define NSAppKitVersionNumber10_5 at file scope (missing from the 10.5 SDK). - Provide cvSetOpenGlDrawCallback/cvSetOpenGlContext/cvUpdateWindow error stubs under HAVE_OPENGL so highgui links when OpenGL interop is enabled with the Cocoa backend. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01NmeP4C25nM6MhcEXEpT6Ae --- modules/highgui/src/window_cocoa.mm | 366 +++++++++++++++++----------- 1 file changed, 224 insertions(+), 142 deletions(-) diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index d68b20b..247e26f 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -75,13 +75,41 @@ CV_IMPL int cvWaitKey (int maxWait) {return 0;} #include +// GCC's Objective-C frontend does not support the @autoreleasepool syntax. +// This file is always compiled without ARC, so an explicit NSAutoreleasePool +// is an exact replacement. Note that returning or jumping out of the pool +// scope is not allowed with the NSAutoreleasePool implementation. +#ifdef __clang__ +#define CV_AUTORELEASE_POOL_BEGIN @autoreleasepool { +#define CV_AUTORELEASE_POOL_END } +#else +#define CV_AUTORELEASE_POOL_BEGIN { NSAutoreleasePool* cvAutoreleasePool = [[NSAutoreleasePool alloc] init]; +#define CV_AUTORELEASE_POOL_END [cvAutoreleasePool drain]; } +#endif + +// Availability checks below use numeric values (1060, 1070) instead of the +// MAC_OS_X_VERSION_10_x constants: pre-10.7 SDKs do not define the later +// constants, and in #if directives an undefined macro silently evaluates to 0, +// selecting the wrong branch. + +// Not defined by the 10.5 SDK +#ifndef NSAppKitVersionNumber10_5 +#define NSAppKitVersionNumber10_5 949 +#endif + const int MIN_SLIDER_WIDTH=200; static NSApplication *application = nil; static NSMutableDictionary *windows = nil; static bool wasInitialized = false; -@interface CVView : NSView +// Explicit ivars are required by the 32-bit ("fragile") Objective-C ABI and by +// GCC, which does not auto-synthesize property backing storage. +@interface CVView : NSView { + NSView *imageView; + NSImage *image; + int sliderHeight; +} @property(retain) NSView *imageView; @property(retain) NSImage *image; @property int sliderHeight; @@ -138,11 +166,7 @@ CV_IMPL int cvInitSystem( int , char** ) application = [NSApplication sharedApplication]; windows = [[NSMutableDictionary alloc] init]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - -#ifndef NSAppKitVersionNumber10_5 -#define NSAppKitVersionNumber10_5 949 -#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 if( floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5 ) [application setActivationPolicy:NSApplicationActivationPolicyRegular]; #endif @@ -153,13 +177,13 @@ CV_IMPL int cvInitSystem( int , char** ) static CVWindow *cvGetWindow(const char *name) { CVWindow *retval = nil; - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN NSString *cvname = [NSString stringWithFormat:@"%s", name]; retval = (CVWindow*) [windows valueForKey:cvname]; if (retval != nil) { [retval retain]; } - } + CV_AUTORELEASE_POOL_END return [retval autorelease]; } @@ -170,33 +194,37 @@ CV_IMPL int cvStartWindowThread() CV_IMPL void cvDestroyWindow( const char* name) { - @autoreleasepool { + CV_AUTORELEASE_POOL_BEGIN CVWindow *window = cvGetWindow(name); if(window) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if ([window styleMask] & NSFullScreenWindowMask) { [window toggleFullScreen:nil]; } +#endif [window close]; [windows removeObjectForKey:[NSString stringWithFormat:@"%s", name]]; } - } + CV_AUTORELEASE_POOL_END } CV_IMPL void cvDestroyAllWindows( void ) { - @autoreleasepool { + CV_AUTORELEASE_POOL_BEGIN NSDictionary* list = [NSDictionary dictionaryWithDictionary:windows]; - for(NSString *key in list) { + NSEnumerator *enumerator = [list keyEnumerator]; + NSString *key; + while((key = [enumerator nextObject]) != nil) { cvDestroyWindow([key cStringUsingEncoding:NSASCIIStringEncoding]); } - } + CV_AUTORELEASE_POOL_END } CV_IMPL void cvShowImage( const char* name, const CvArr* arr) { - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN CVWindow *window = cvGetWindow(name); if(!window) { @@ -221,21 +249,25 @@ CV_IMPL void cvShowImage( const char* name, const CvArr* arr) if ([[window contentView] respondsToSelector:@selector(convertSizeFromBacking:)]) { // Only resize for retina displays if the image is bigger than the screen - NSSize screenSize = NSScreen.mainScreen.visibleFrame.size; - CGFloat titleBarHeight = window.frame.size.height - [window contentRectForFrameRect:window.frame].size.height; + NSSize screenSize = [[NSScreen mainScreen] visibleFrame].size; + CGFloat titleBarHeight = [window frame].size.height - [window contentRectForFrameRect:[window frame]].size.height; screenSize.height -= titleBarHeight; if (imageSize.width > screenSize.width || imageSize.height > screenSize.height) { CGFloat fx = screenSize.width/std::max(imageSize.width, (CGFloat)1.f); CGFloat fy = screenSize.height/std::max(imageSize.height, (CGFloat)1.f); CGFloat min_f = std::min(fx, fy); - scaledImageSize = [[window contentView] convertSizeFromBacking:imageSize]; + // -convertSizeFromBacking: is not declared in pre-10.7 SDKs; + // call it through a typed IMP so the struct-return ABI is correct. + typedef NSSize (*ConvertSizeFunc)(id, SEL, NSSize); + ConvertSizeFunc convertFunc = (ConvertSizeFunc)[[window contentView] methodForSelector:@selector(convertSizeFromBacking:)]; + scaledImageSize = convertFunc([window contentView], @selector(convertSizeFromBacking:), imageSize); scaledImageSize.width = std::min(scaledImageSize.width, min_f*imageSize.width); scaledImageSize.height = std::min(scaledImageSize.height, min_f*imageSize.height); } } NSSize contentSize = vrectOld.size; - contentSize.height = scaledImageSize.height + [window contentView].sliderHeight; + contentSize.height = scaledImageSize.height + [[window contentView] sliderHeight]; contentSize.width = std::max(scaledImageSize.width, MIN_SLIDER_WIDTH); [window setContentSize:contentSize]; //adjust sliders to fit new window size if([window firstContent]) @@ -253,19 +285,19 @@ CV_IMPL void cvShowImage( const char* name, const CvArr* arr) } [window setFirstContent:NO]; } - } + CV_AUTORELEASE_POOL_END } CV_IMPL void cvResizeWindow( const char* name, int width, int height) { - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN CVWindow *window = cvGetWindow(name); if(window && ![window autosize]) { - height += [window contentView].sliderHeight; + height += [[window contentView] sliderHeight]; NSSize size = { (CGFloat)width, (CGFloat)height }; [window setContentSize:size]; } - } + CV_AUTORELEASE_POOL_END } CV_IMPL void cvMoveWindow( const char* name, int x, int y) @@ -278,7 +310,7 @@ CV_IMPL void cvMoveWindow( const char* name, int x, int y) if(name == NULL) CV_ERROR( CV_StsNullPtr, "NULL window name" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(name); if(window) { if([window firstContent]) { @@ -291,7 +323,7 @@ CV_IMPL void cvMoveWindow( const char* name, int x, int y) [window setFrameTopLeftPoint:NSMakePoint(x, y)]; } } - } + CV_AUTORELEASE_POOL_END __END__; } @@ -308,7 +340,7 @@ CV_IMPL int cvCreateTrackbar (const char* trackbar_name, __BEGIN__; if(window_name == NULL) CV_ERROR( CV_StsNullPtr, "NULL window name" ); - @autoreleasepool { + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(window_name); if(window) { [window createSliderWithName:trackbar_name @@ -317,7 +349,7 @@ CV_IMPL int cvCreateTrackbar (const char* trackbar_name, callback:on_notify]; result = 1; } - } + CV_AUTORELEASE_POOL_END __END__; return result; } @@ -330,7 +362,7 @@ CV_IMPL int cvCreateTrackbar2(const char* trackbar_name, void* userdata) { int res = cvCreateTrackbar(trackbar_name, window_name, val, count, NULL); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN if(res) { CVWindow *window = cvGetWindow(window_name); if (window && [window respondsToSelector:@selector(sliders)]) { @@ -339,7 +371,7 @@ CV_IMPL int cvCreateTrackbar2(const char* trackbar_name, [slider setUserData:userdata]; } } - } + CV_AUTORELEASE_POOL_END return res; } @@ -353,13 +385,13 @@ cvSetMouseCallback( const char* name, CvMouseCallback function, void* info) __BEGIN__; if(name == NULL) CV_ERROR( CV_StsNullPtr, "NULL window name" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(name); if(window) { [window setMouseCallback:function]; [window setMouseParam:info]; } - } + CV_AUTORELEASE_POOL_END __END__; } @@ -374,7 +406,7 @@ cvSetMouseCallback( const char* name, CvMouseCallback function, void* info) if(trackbar_name == NULL || window_name == NULL) CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(window_name); if(window && [window respondsToSelector:@selector(sliders)]) { CVSlider *slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]]; @@ -382,7 +414,7 @@ cvSetMouseCallback( const char* name, CvMouseCallback function, void* info) pos = [[slider slider] intValue]; } } - } + CV_AUTORELEASE_POOL_END __END__; return pos; } @@ -401,7 +433,7 @@ CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name if(pos < 0) CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(window_name); if(window && [window respondsToSelector:@selector(sliders)]) { slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]]; @@ -412,7 +444,7 @@ CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name } } } - } + CV_AUTORELEASE_POOL_END __END__; } @@ -427,7 +459,7 @@ CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name if(trackbar_name == NULL || window_name == NULL) CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(window_name); if(window && [window respondsToSelector:@selector(sliders)]) { slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]]; @@ -439,7 +471,7 @@ CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name } } } - } + CV_AUTORELEASE_POOL_END __END__; } @@ -454,7 +486,7 @@ CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name if(trackbar_name == NULL || window_name == NULL) CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN window = cvGetWindow(window_name); if(window && [window respondsToSelector:@selector(sliders)]) { slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]]; @@ -466,7 +498,7 @@ CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name } } } - } + CV_AUTORELEASE_POOL_END __END__; } @@ -478,14 +510,18 @@ CV_IMPL void* cvGetWindowHandle( const char* name ) CV_IMPL const char* cvGetWindowName( void* window_handle ) { - @autoreleasepool{ - for(NSString *key in windows) { + const char* name = 0; + CV_AUTORELEASE_POOL_BEGIN + NSEnumerator *enumerator = [windows keyEnumerator]; + NSString *key; + while((key = [enumerator nextObject]) != nil) { if([windows valueForKey:key] == window_handle) { - return [key UTF8String]; + name = [key UTF8String]; + break; } } - } - return 0; + CV_AUTORELEASE_POOL_END + return name; } CV_IMPL int cvNamedWindow( const char* name, int flags ) @@ -493,58 +529,62 @@ CV_IMPL int cvNamedWindow( const char* name, int flags ) if( !wasInitialized ) cvInitSystem(0, 0); - @autoreleasepool{ + int result = -1; + CV_AUTORELEASE_POOL_BEGIN CVWindow *window = cvGetWindow(name); if( window ) { [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)]; - return 0; - } - - NSScreen* mainDisplay = [NSScreen mainScreen]; - NSString *windowName = [NSString stringWithFormat:@"%s", name]; - NSUInteger showResize = NSResizableWindowMask; - NSUInteger styleMask = NSTitledWindowMask|NSMiniaturizableWindowMask|showResize; - CGFloat windowWidth = [NSWindow minFrameWidthWithTitle:windowName styleMask:styleMask]; - NSRect initContentRect = NSMakeRect(0, 0, windowWidth, 0); - if (mainDisplay) { - NSRect dispFrame = [mainDisplay visibleFrame]; - initContentRect.origin.y = dispFrame.size.height-20; + result = 0; } + else + { + NSScreen* mainDisplay = [NSScreen mainScreen]; + NSString *windowName = [NSString stringWithFormat:@"%s", name]; + NSUInteger showResize = NSResizableWindowMask; + NSUInteger styleMask = NSTitledWindowMask|NSMiniaturizableWindowMask|showResize; + CGFloat windowWidth = [NSWindow minFrameWidthWithTitle:windowName styleMask:styleMask]; + NSRect initContentRect = NSMakeRect(0, 0, windowWidth, 0); + if (mainDisplay) { + NSRect dispFrame = [mainDisplay visibleFrame]; + initContentRect.origin.y = dispFrame.size.height-20; + } - window = [[CVWindow alloc] initWithContentRect:initContentRect - styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|showResize - backing:NSBackingStoreBuffered - defer:YES - screen:mainDisplay]; + window = [[CVWindow alloc] initWithContentRect:initContentRect + styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|showResize + backing:NSBackingStoreBuffered + defer:YES + screen:mainDisplay]; - [window setFrameTopLeftPoint:initContentRect.origin]; + [window setFrameTopLeftPoint:initContentRect.origin]; - [window setFirstContent:YES]; - [window setX0:-1]; - [window setY0:-1]; + [window setFirstContent:YES]; + [window setX0:-1]; + [window setY0:-1]; - [window setContentView:[[CVView alloc] init]]; + [window setContentView:[[CVView alloc] init]]; - [NSApp activateIgnoringOtherApps:YES]; + [NSApp activateIgnoringOtherApps:YES]; - [window setHasShadow:YES]; - [window setAcceptsMouseMovedEvents:YES]; - [window useOptimizedDrawing:YES]; - [window setTitle:windowName]; - [window makeKeyAndOrderFront:nil]; + [window setHasShadow:YES]; + [window setAcceptsMouseMovedEvents:YES]; + [window useOptimizedDrawing:YES]; + [window setTitle:windowName]; + [window makeKeyAndOrderFront:nil]; - [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)]; + [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)]; - [windows setValue:window forKey:windowName]; - return [windows count]-1; - } + [windows setValue:window forKey:windowName]; + result = [windows count]-1; + } + CV_AUTORELEASE_POOL_END + return result; } CV_IMPL int cvWaitKey (int maxWait) { int returnCode = -1; - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN double start = [[NSDate date] timeIntervalSince1970]; while(true) { @@ -568,8 +608,8 @@ CV_IMPL int cvWaitKey (int maxWait) [NSThread sleepForTimeInterval:1/100.]; } - return returnCode; - } + CV_AUTORELEASE_POOL_END + return returnCode; } CvRect cvGetWindowRect_COCOA( const char* name ) @@ -590,16 +630,16 @@ CvRect cvGetWindowRect_COCOA( const char* name ) { CV_ERROR( CV_StsNullPtr, "NULL window" ); } else { - @autoreleasepool { + CV_AUTORELEASE_POOL_BEGIN NSRect rect = [window frame]; -#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6 +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 NSPoint pt = [window convertRectToScreen:rect].origin; #else NSPoint pt = [window convertBaseToScreen:rect.origin]; #endif NSSize sz = [[[window contentView] image] size]; result = cvRect(pt.x, pt.y, sz.width, sz.height); - } + CV_AUTORELEASE_POOL_END } __END__; return result; @@ -624,7 +664,7 @@ double cvGetModeWindow_COCOA( const char* name ) CV_ERROR( CV_StsNullPtr, "NULL window" ); } - result = window.status; + result = [window status]; __END__; return result; } @@ -633,7 +673,7 @@ void cvSetModeWindow_COCOA( const char* name, double prop_value ) { CVWindow *window = nil; -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 NSDictionary *fullscreenOptions = nil; #endif @@ -656,13 +696,13 @@ void cvSetModeWindow_COCOA( const char* name, double prop_value ) return; } - @autoreleasepool{ -#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6 + CV_AUTORELEASE_POOL_BEGIN +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if ( ([window styleMask] & NSFullScreenWindowMask) && prop_value==CV_WINDOW_NORMAL ) { [window toggleFullScreen:nil]; - window.status=CV_WINDOW_NORMAL; + [window setStatus:CV_WINDOW_NORMAL]; } else if( !([window styleMask] & NSFullScreenWindowMask) && prop_value==CV_WINDOW_FULLSCREEN ) { @@ -679,22 +719,22 @@ void cvSetModeWindow_COCOA( const char* name, double prop_value ) [window setFrameTopLeftPoint: frame.origin]; - window.status=CV_WINDOW_FULLSCREEN; + [window setStatus:CV_WINDOW_FULLSCREEN]; } #else fullscreenOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:NSFullScreenModeSetting]; if ( [[window contentView] isInFullScreenMode] && prop_value==CV_WINDOW_NORMAL ) { [[window contentView] exitFullScreenModeWithOptions:fullscreenOptions]; - window.status=CV_WINDOW_NORMAL; + [window setStatus:CV_WINDOW_NORMAL]; } else if( ![[window contentView] isInFullScreenMode] && prop_value==CV_WINDOW_FULLSCREEN ) { [[window contentView] enterFullScreenMode:[NSScreen mainScreen] withOptions:fullscreenOptions]; - window.status=CV_WINDOW_FULLSCREEN; + [window setStatus:CV_WINDOW_FULLSCREEN]; } #endif - } + CV_AUTORELEASE_POOL_END __END__; } @@ -718,7 +758,7 @@ double cvGetPropVisible_COCOA(const char* name) CV_ERROR(CV_StsNullPtr, "NULL window"); } - result = window.isVisible ? 1 : 0; + result = [window isVisible] ? 1 : 0; __END__; return result; @@ -743,7 +783,7 @@ double cvGetPropTopmost_COCOA(const char* name) CV_ERROR(CV_StsNullPtr, "NULL window"); } - result = (window.level == NSStatusWindowLevel) ? 1 : 0; + result = ([window level] == NSStatusWindowLevel) ? 1 : 0; __END__; return result; @@ -759,35 +799,37 @@ void cvSetPropTopmost_COCOA( const char* name, const bool topmost ) { CV_ERROR( CV_StsNullPtr, "NULL name string" ); } - @autoreleasepool{ - window = cvGetWindow(name); - if ( window == NULL ) - { - CV_ERROR( CV_StsNullPtr, "NULL window" ); - } - - if (([window styleMask] & NSFullScreenWindowMask)) - { - EXIT; - } + window = cvGetWindow(name); + if ( window == NULL ) + { + CV_ERROR( CV_StsNullPtr, "NULL window" ); + } - if (topmost) - { - [window makeKeyAndOrderFront:window.self]; - [window setLevel:CGWindowLevelForKey(kCGMaximumWindowLevelKey)]; - } - else + CV_AUTORELEASE_POOL_BEGIN +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + const bool isFullScreen = ([window styleMask] & NSFullScreenWindowMask) != 0; +#else + const bool isFullScreen = false; +#endif + if (!isFullScreen) { - [window makeKeyAndOrderFront:nil]; + if (topmost) + { + [window makeKeyAndOrderFront:window]; + [window setLevel:CGWindowLevelForKey(kCGMaximumWindowLevelKey)]; + } + else + { + [window makeKeyAndOrderFront:nil]; + } } - } + CV_AUTORELEASE_POOL_END __END__; } void setWindowTitle_COCOA(const cv::String& winname, const cv::String& title) { - - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN CVWindow *window = cvGetWindow(winname.c_str()); if (window == NULL) @@ -801,10 +843,33 @@ void setWindowTitle_COCOA(const cv::String& winname, const cv::String& title) NSString *windowTitle = [NSString stringWithFormat:@"%s", title.c_str()]; [window setTitle:windowTitle]; - } + CV_AUTORELEASE_POOL_END +} +#ifdef HAVE_OPENGL + +// The Cocoa backend does not implement OpenGL windows. These stubs make +// highgui link when OpenCV is configured with WITH_OPENGL=ON (which enables +// cv::ogl interoperability in the core module); window-related OpenGL +// functionality remains unavailable. + +CV_IMPL void cvSetOpenGlDrawCallback(const char*, CvOpenGlDrawCallback, void*) +{ + CV_Error(cv::Error::OpenGlNotSupported, "OpenGL windows are not supported by the Cocoa backend"); } +CV_IMPL void cvSetOpenGlContext(const char*) +{ + CV_Error(cv::Error::OpenGlNotSupported, "OpenGL windows are not supported by the Cocoa backend"); +} + +CV_IMPL void cvUpdateWindow(const char*) +{ + CV_Error(cv::Error::OpenGlNotSupported, "OpenGL windows are not supported by the Cocoa backend"); +} + +#endif // HAVE_OPENGL + static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { CGFloat heightDiff = (base.height / constraint.height); CGFloat widthDiff = (base.width / constraint.width); @@ -837,28 +902,40 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { - (void)cvSendMouseEvent:(NSEvent *)event type:(int)type flags:(int)flags { (void)event; NSPoint mp = [NSEvent mouseLocation]; - mp = [self convertScreenToBase: mp]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([self respondsToSelector:@selector(convertRectFromScreen:)]) { + NSRect screenRect = NSMakeRect(mp.x, mp.y, 0, 0); + mp = [self convertRectFromScreen:screenRect].origin; + } else +#endif + { + mp = [self convertScreenToBase: mp]; + } CVView *contentView = [self contentView]; NSSize viewSize = contentView.frame.size; - if (contentView.imageView) { - viewSize = contentView.imageView.frame.size; + if ([contentView imageView]) { + viewSize = [[contentView imageView] frame].size; } else { - viewSize.height -= contentView.sliderHeight; + viewSize.height -= [contentView sliderHeight]; } mp.y = viewSize.height - mp.y; - NSSize imageSize = contentView.image.size; - mp.y *= (imageSize.height / std::max(viewSize.height, 1.)); - mp.x *= (imageSize.width / std::max(viewSize.width, 1.)); - - if( [event type] == NSEventTypeScrollWheel ) { - if( event.hasPreciseScrollingDeltas ) { - mp.x = int(event.scrollingDeltaX); - mp.y = int(event.scrollingDeltaY); - } else { - mp.x = int(event.scrollingDeltaX / 0.100006); - mp.y = int(event.scrollingDeltaY / 0.100006); + NSSize imageSize = [[contentView image] size]; + mp.y *= (imageSize.height / std::max(viewSize.height, (CGFloat)1.)); + mp.x *= (imageSize.width / std::max(viewSize.width, (CGFloat)1.)); + + if( [event type] == NSScrollWheel ) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + // scrollingDelta* are 10.7+ APIs; fall back to line-based deltas below + if( [event respondsToSelector:@selector(hasPreciseScrollingDeltas)] && [event hasPreciseScrollingDeltas] ) { + mp.x = int([event scrollingDeltaX]); + mp.y = int([event scrollingDeltaY]); + } else +#endif + { + mp.x = int([event deltaX] * 10.0); + mp.y = int([event deltaY] * 10.0); } if( mp.x && !mp.y && cv::EVENT_MOUSEWHEEL == type ) { type = cv::EVENT_MOUSEHWHEEL; @@ -899,7 +976,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { if([event type] == NSLeftMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_LBUTTON];} if([event type] == NSRightMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_RBUTTON];} if([event type] == NSOtherMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_MBUTTON];} - if([event type] == NSEventTypeScrollWheel) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEWHEEL flags:flags ];} + if([event type] == NSScrollWheel) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEWHEEL flags:flags ];} } -(void)scrollWheel:(NSEvent *)theEvent { @@ -955,7 +1032,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { // Create slider CVSlider *slider = [[CVSlider alloc] init]; [[slider name] setStringValue:cvname]; - slider.initialName = [NSString stringWithFormat:@"%s", name]; + [slider setInitialName:[NSString stringWithFormat:@"%s", name]]; [[slider slider] setMaxValue:max]; [[slider slider] setMinValue:0]; if(value) @@ -981,7 +1058,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { viewSize.width = std::max(viewSize.width, MIN_SLIDER_WIDTH); // Update slider sizes - [self contentView].sliderHeight += sliderSize.height; + [[self contentView] setSliderHeight:[[self contentView] sliderHeight] + sliderSize.height]; if ([[self contentView] image] && ![[self contentView] imageView]) { [[self contentView] setNeedsDisplay:YES]; @@ -999,7 +1076,9 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { @implementation CVView +@synthesize imageView; @synthesize image; +@synthesize sliderHeight; - (id)init { [super init]; @@ -1008,7 +1087,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { - (void)setImageData:(CvArr *)arr { cv::Mat arrMat = cv::cvarrToMat(arr); - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:arrMat.cols pixelsHigh:arrMat.rows @@ -1070,7 +1149,7 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { redisplayRect.size.height -= [self sliderHeight]; [self setNeedsDisplayInRect:redisplayRect]; } - } + CV_AUTORELEASE_POOL_END } - (void)setFrameSize:(NSSize)size { @@ -1078,19 +1157,22 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { int height = size.height; - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN CVWindow *cvwindow = (CVWindow *)[self window]; if ([cvwindow respondsToSelector:@selector(sliders)]) { - for(NSString *key in [cvwindow slidersKeys]) { + NSEnumerator *enumerator = [[cvwindow slidersKeys] objectEnumerator]; + NSString *key; + while((key = [enumerator nextObject]) != nil) { CVSlider *slider = [[cvwindow sliders] valueForKey:key]; NSRect r = [slider frame]; r.origin.y = height - r.size.height; r.size.width = [[cvwindow contentView] frame].size.width; - CGRect sliderRect = slider.slider.frame; + // NSRect and CGRect are distinct types in 32-bit builds + NSRect sliderRect = [[slider slider] frame]; CGFloat targetWidth = r.size.width - (sliderRect.origin.x + 10); sliderRect.size.width = targetWidth < 0 ? 0 : targetWidth; - slider.slider.frame = sliderRect; + [[slider slider] setFrame:sliderRect]; [slider setFrame:r]; height -= r.size.height; @@ -1107,21 +1189,21 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { NSRect constrainedFrame = { imageViewFrame.origin, constrainAspectRatio(imageViewFrame.size, [image size]) }; [[self imageView] setFrame:constrainedFrame]; } - } + CV_AUTORELEASE_POOL_END } - (void)drawRect:(NSRect)rect { [super drawRect:rect]; // If imageView exists, all drawing will be done by it and nothing needs to happen here if ([self image] && ![self imageView]) { - @autoreleasepool{ + CV_AUTORELEASE_POOL_BEGIN if(image != nil) { [image drawInRect: [self frame] fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0]; } - } + CV_AUTORELEASE_POOL_END } }