osxvideosink: start internal window if no view is provided
authorAndoni Morales Alastruey <ylatuya@gmail.com>
Mon, 14 May 2012 16:01:02 +0000 (18:01 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 24 May 2012 12:35:27 +0000 (14:35 +0200)
sys/osxvideo/cocoawindow.h
sys/osxvideo/cocoawindow.m
sys/osxvideo/osxvideosink.h
sys/osxvideo/osxvideosink.m

index 7eec953..04f64c8 100644 (file)
@@ -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
index b4f986a..543d5cd 100644 (file)
@@ -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
index c74416f..4a68b13 100644 (file)
@@ -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 <NSWindowDelegate>
+{
+  @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;
index c4a8467..9fbe533 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "config.h"
 #include <gst/interfaces/xoverlay.h>
+#include <gst/interfaces/navigation.h>
 
 #include "osxvideosink.h"
 #include <unistd.h>
@@ -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