diff --git a/sys/osxvideo/cocoawindow.h b/sys/osxvideo/cocoawindow.h index 7eec953333..04f64c8229 100644 --- a/sys/osxvideo/cocoawindow.h +++ b/sys/osxvideo/cocoawindow.h @@ -74,5 +74,5 @@ struct _GstOSXImage; - (void) setContentSize: (NSSize) size; - (GstGLView *) gstView; -- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)aScreen; +- (id)initWithContentNSRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)aScreen; @end diff --git a/sys/osxvideo/cocoawindow.m b/sys/osxvideo/cocoawindow.m index b4f986add1..543d5cdd8b 100644 --- a/sys/osxvideo/cocoawindow.m +++ b/sys/osxvideo/cocoawindow.m @@ -42,7 +42,7 @@ @ implementation GstOSXVideoSinkWindow /* The object has to be released */ -- (id) initWithContentRect: (NSRect) rect +- (id) initWithContentNSRect: (NSRect) rect styleMask: (unsigned int) styleMask backing: (NSBackingStoreType) bufferingType defer: (BOOL) flag diff --git a/sys/osxvideo/osxvideosink.h b/sys/osxvideo/osxvideosink.h index c74416fa08..4a68b13378 100644 --- a/sys/osxvideo/osxvideosink.h +++ b/sys/osxvideo/osxvideosink.h @@ -62,16 +62,20 @@ typedef struct _GstOSXVideoSinkClass GstOSXVideoSinkClass; /* OSXWindow stuff */ struct _GstOSXWindow { gint width, height; + gboolean closed; gboolean internal; GstGLView* gstview; + GstOSXVideoSinkWindow* win; }; struct _GstOSXVideoSink { /* Our element stuff */ GstVideoSink videosink; GstOSXWindow *osxwindow; + gboolean app_started; void *osxvideosinkobject; NSView *superview; + guint cocoa_timeout; }; struct _GstOSXVideoSinkClass { @@ -80,6 +84,12 @@ struct _GstOSXVideoSinkClass { GType gst_osx_video_sink_get_type(void); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 +@interface NSApplication(AppleMenu) +- (void)setAppleMenu:(NSMenu *)menu; +@end +#endif + @interface GstBufferObject : NSObject { @public @@ -90,13 +100,24 @@ GType gst_osx_video_sink_get_type(void); @end +@interface GstWindowDelegate : NSObject +{ + @public + GstOSXVideoSink *osxvideosink; +} +-(id) initWithSink: (GstOSXVideoSink *) sink; +@end + @interface GstOSXVideoSinkObject : NSObject { + BOOL destroyed; + @public GstOSXVideoSink *osxvideosink; } -(id) initWithSink: (GstOSXVideoSink *) sink; +-(void) createInternalWindow; -(void) resize; -(void) destroy; -(void) showFrame: (GstBufferObject*) buf; diff --git a/sys/osxvideo/osxvideosink.m b/sys/osxvideo/osxvideosink.m index c4a8467999..9fbe53358b 100644 --- a/sys/osxvideo/osxvideosink.m +++ b/sys/osxvideo/osxvideosink.m @@ -38,6 +38,7 @@ #include "config.h" #include +#include #include "osxvideosink.h" #include @@ -83,6 +84,130 @@ gst_osx_video_sink_call_from_main_thread(NSObject * object, SEL function, [pool release]; } +static NSString * +GetApplicationName(void) +{ + NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} + +static void +CreateApplicationMenus(void) +{ + NSString *appName; + NSString *title; + NSMenu *appleMenu; + NSMenu *windowMenu; + NSMenuItem *menuItem; + + /* Create the main menu bar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + + /* Create the application menu */ + appName = GetApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/""]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/""]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + [appleMenu release]; + + + /* Create the window menu */ + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@/*"m"*/""]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + [windowMenu release]; +} + +/* Poll for cocoa events */ +static void +cocoa_poll_events (void) { + NSEvent *event; + NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init]; + + do { + event =[NSApp nextEventMatchingMask: NSAnyEventMask untilDate: + [NSDate distantPast] inMode: + NSDefaultRunLoopMode dequeue:YES]; + [NSApp sendEvent:event]; + } + while (event != nil); + [pool release]; +} + +static void +gst_osx_video_sink_start_cocoa_event_poller (GstOSXVideoSink * osxvideosink ) +{ + /* Cocoa applications require a main run loop running to dispatch UI + * events and process deferred calls to the main thread through + * perfermSelectorOnMainThread. + * Since the sink needs to create it's own Cocoa window when no + * external NSView is passed to the sink through the GstXOverlay API, + * we need to replace cocoa's main run loop with a poller. + * We are also assuming that when the main run loop is not running + * (nor native Cocoa, nor GTK, nor QT), there is at least glib's main + * loop running. + */ + if ([[NSRunLoop mainRunLoop] currentMode] == nil) { + osxvideosink->cocoa_timeout = g_timeout_add (10, + (GSourceFunc) cocoa_poll_events, NULL); + } +} + +static void +gst_osx_video_sink_stop_cocoa_event_poller (GstOSXVideoSink * osxvideosink) +{ + if (osxvideosink->cocoa_timeout) + g_source_remove(osxvideosink->cocoa_timeout); +} + /* This function handles osx window creation */ static gboolean gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width, @@ -103,6 +228,7 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width, osxwindow->width = width; osxwindow->height = height; + osxwindow->closed = FALSE; /* Allocate our GstGLView for the window, and then tell the application * about it (hopefully it's listening...) */ @@ -145,6 +271,9 @@ gst_osx_video_sink_osxwindow_create (GstOSXVideoSink * osxvideosink, gint width, * application handled have-ns-view, stored our view internally and is * going to add it to a superview later (webkit does that now). */ + gst_osx_video_sink_start_cocoa_event_poller (osxvideosink); + gst_osx_video_sink_call_from_main_thread(osxvideosink->osxvideosinkobject, + @selector(createInternalWindow), nil, YES); GST_INFO_OBJECT (osxvideosink, "no superview"); } } @@ -165,6 +294,7 @@ gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink) gst_osx_video_sink_call_from_main_thread(osxvideosink->osxvideosinkobject, @selector(destroy), (id) nil, YES); + gst_osx_video_sink_stop_cocoa_event_poller(osxvideosink); [pool release]; } @@ -350,6 +480,7 @@ gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink) osxvideosink->superview = NULL; osxvideosink->osxvideosinkobject = [[GstOSXVideoSinkObject alloc] initWithSink:osxvideosink]; + osxvideosink->app_started = FALSE; } static void @@ -579,11 +710,33 @@ gst_osx_video_sink_get_type (void) GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info); g_type_add_interface_static (osxvideosink_type, GST_TYPE_X_OVERLAY, &overlay_info); + g_type_add_interface_static (osxvideosink_type, GST_TYPE_NAVIGATION, + &navigation_info); } return osxvideosink_type; } +@implementation GstWindowDelegate +- (id) initWithSink: (GstOSXVideoSink *) sink +{ + self = [super init]; + self->osxvideosink = sink; + return self; +} + +- (void)windowWillClose:(NSNotification *)notification { + /* Only handle close events if the window was closed manually by the user + * and not becuase of a state change state to READY */ + if (!osxvideosink->osxwindow->closed) { + osxvideosink->osxwindow->closed = TRUE; + GST_ELEMENT_ERROR (osxvideosink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL)); + gst_osx_video_sink_osxwindow_destroy(osxvideosink); + } +} + +@end + @ implementation GstOSXVideoSinkObject -(id) initWithSink: (GstOSXVideoSink*) sink @@ -593,6 +746,52 @@ gst_osx_video_sink_get_type (void) return self; } +-(void) createInternalWindow +{ + GstOSXWindow *osxwindow = osxvideosink->osxwindow; + ProcessSerialNumber psn; + NSRect rect; + unsigned int mask; + + osxwindow->internal = TRUE; + + mask = NSTitledWindowMask | + NSClosableWindowMask | + NSResizableWindowMask | + NSTexturedBackgroundWindowMask | + NSMiniaturizableWindowMask; + + rect.origin.x = 100.0; + rect.origin.y = 100.0; + rect.size.width = (float) osxwindow->width; + rect.size.height = (float) osxwindow->height; + + if (!osxvideosink->app_started) { + CreateApplicationMenus(); + [NSApplication sharedApplication]; + [NSApp finishLaunching]; + osxvideosink->app_started = TRUE; + } + + if (!GetCurrentProcess(&psn)) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } + + osxwindow->win =[[GstOSXVideoSinkWindow alloc] + initWithContentNSRect: rect + styleMask: mask + backing: NSBackingStoreBuffered + defer: NO + screen: nil]; + GST_DEBUG("VideoSinkWindow created, %p", osxwindow->win); + [osxwindow->win makeKeyAndOrderFront:NSApp]; + osxwindow->gstview =[osxwindow->win gstView]; + [osxwindow->win setDelegate:[[GstWindowDelegate alloc] + initWithSink:osxvideosink]]; + +} + - (void) resize { GstOSXWindow *osxwindow = osxvideosink->osxwindow; @@ -612,7 +811,7 @@ gst_osx_video_sink_get_type (void) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; GstBuffer *buf = object->buf; - if (osxvideosink->osxwindow && self->osxvideosink->osxvideosinkobject) + if (!destroyed) { viewdata = (guint8 *) [osxvideosink->osxwindow->gstview getTextureBuffer]; @@ -631,17 +830,26 @@ gst_osx_video_sink_get_type (void) pool = [[NSAutoreleasePool alloc] init]; + destroyed = TRUE; + if (osxvideosink->osxwindow) { if (osxvideosink->superview) { [osxvideosink->osxwindow->gstview removeFromSuperview]; } [osxvideosink->osxwindow->gstview release]; + if (osxvideosink->osxwindow->internal) { + if (!osxvideosink->osxwindow->closed) { + osxvideosink->osxwindow->closed = TRUE; + [osxvideosink->osxwindow->win release]; + } + } g_free (osxvideosink->osxwindow); osxvideosink->osxwindow = NULL; } [pool release]; } + @end @ implementation GstBufferObject