sys/osxvideo/: Fix leaks when running a NSApp.
authorEdward Hervey <bilboed@bilboed.com>
Wed, 14 Mar 2007 16:30:19 +0000 (16:30 +0000)
committerEdward Hervey <bilboed@bilboed.com>
Wed, 14 Mar 2007 16:30:19 +0000 (16:30 +0000)
Original commit message from CVS:
* sys/osxvideo/cocoawindow.h:
* sys/osxvideo/cocoawindow.m:
* sys/osxvideo/osxvideosink.h:
* sys/osxvideo/osxvideosink.m:
Fix leaks when running a NSApp.
Accept any kind of resolutions.
Works in fullscreen. Can maximize.
Only thing left before being able to move this to -good is documentation
and embedded window support.

sys/osxvideo/cocoawindow.h
sys/osxvideo/cocoawindow.m
sys/osxvideo/osxvideosink.h
sys/osxvideo/osxvideosink.m

index f479c99..340bd16 100644 (file)
@@ -43,6 +43,7 @@ struct _GstOSXImage;
     int width, height;
     BOOL fullscreen;
     NSOpenGLContext* fullScreenContext; 
+    NSOpenGLContext* actualContext;
 }
 - (void) drawQuad;
 - (void) drawRect: (NSRect) rect;
index e191de4..9b5e7b3 100644 (file)
@@ -41,6 +41,7 @@
 
 @ implementation GstOSXVideoSinkWindow
 
+/* The object has to be released */
 - (id) initWithContentRect: (NSRect) rect
                 styleMask: (unsigned int) styleMask
                   backing: (NSBackingStoreType) bufferingType 
@@ -84,7 +85,7 @@
 - (void) sendEvent:(NSEvent *) event {
   BOOL taken = NO;
 
-  GST_LOG ("event %p type:%d", event,[event type]);
+  GST_DEBUG ("event %p type:%d", event,[event type]);
 
   if ([event type] == NSKeyDown) {
   }
 
   self = [super initWithFrame: frame pixelFormat:fmt];
 
-  [[self openGLContext] makeCurrentContext];
-  [[self openGLContext] update];
+   actualContext = [self openGLContext];
+   [actualContext makeCurrentContext];
+   [actualContext update];
 
   /* Black background */
   glClearColor (0.0, 0.0, 0.0, 0.0);
 
   pi_texture = 0;
-  data = g_malloc (2 * 320 * 240);
+  data = nil;
   width = frame.size.width;
   height = frame.size.height;
 
     return;
   }
 
-  [[self openGLContext] makeCurrentContext];
+  [actualContext makeCurrentContext];
 
   bounds = [self bounds];
 
 }
 
 - (void) initTextures {
-  NSOpenGLContext *currentContext;
 
-  if (fullscreen)
-    currentContext = fullScreenContext;
-  else
-    currentContext =[self openGLContext];
-
-  [currentContext makeCurrentContext];
+  [actualContext makeCurrentContext];
 
   /* Free previous texture if any */
-  if (initDone) {
+  if (pi_texture) {
     glDeleteTextures (1, &pi_texture);
   }
+
+  if (data) {
+    data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
+  } else {
+    data = g_malloc0(width * height * sizeof(short));
+  }
   /* Create textures */
   glGenTextures (1, &pi_texture);
 
   glEnable (GL_TEXTURE_RECTANGLE_EXT);
   glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
 
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
+  
   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
 
   /* Use VRAM texturing */
      our buffer */
   glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
 
-
   /* Linear interpolation */
   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                   GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
                   GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-  glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+  // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
 
   glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
-               width, height, 0, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
+               width, height, 0, 
+               GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
 
 
   initDone = 1;
 }
 
 - (void) reloadTexture {
-  NSOpenGLContext *currentContext;
-
   if (!initDone) {
     return;
   }
 
   GST_LOG ("Reloading Texture");
 
-  if (fullscreen)
-    currentContext = fullScreenContext;
-  else
-    currentContext =[self openGLContext];
-  [currentContext makeCurrentContext];
+  [actualContext makeCurrentContext];
 
   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
 
   /* glTexSubImage2D is faster than glTexImage2D
      http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
      TextureRange/MainOpenGLView.m.htm */
-  glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, width, height, GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);    //FIXME
+  glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
+                  width, height,
+                  GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);    //FIXME
 }
 
 - (void) cleanUp {
 }
 
 - (void) drawRect:(NSRect) rect {
-  NSOpenGLContext *currentContext;
   long params[] = { 1 };
 
-  if (fullscreen) {
-    currentContext = fullScreenContext;
-  } else {
-    currentContext =[self openGLContext];
-  }
-  [currentContext makeCurrentContext];
+  [actualContext makeCurrentContext];
 
   CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
 
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
   if (!initDone) {
-    [[self openGLContext] flushBuffer];
+    [actualContext flushBuffer];
     return;
   }
 
   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
   [self drawQuad];
   /* Draw */
-  [currentContext flushBuffer];
+  [actualContext flushBuffer];
 }
 
 - (void) displayTexture {
       return;
     }
 
+    actualContext = fullScreenContext;
+
     /* Capture display, switch to fullscreen */
     if (CGCaptureAllDisplays () != CGDisplayNoErr) {
       GST_WARNING ("CGCaptureAllDisplays() failed");
   } else if (fullscreen && !flag) {
     // fullscreen now and needs to go back to normal
     initDone = NO;
+    
+    actualContext = [self openGLContext];
+
     [NSOpenGLContext clearCurrentContext];
+    [fullScreenContext clearDrawable];
+    [fullScreenContext release];
+    fullScreenContext = nil;
 
     CGReleaseAllDisplays ();
 
   width = w;
   height = h;
 
-  // FIXME : so, do we free, or don't we ?
-  //if (data) g_free(data);
+//  if (data) g_free(data);
 
-  data = g_malloc0 (2 * w * h);
-  [self reloadTexture];
+//  data = g_malloc0 (2 * w * h);
+  [self initTextures];
 }
 
+- (void) dealloc {
+  GST_LOG ("dealloc called");
+  if (data) g_free(data);
+
+  if (fullScreenContext) {
+    [NSOpenGLContext clearCurrentContext];
+    [fullScreenContext clearDrawable];
+    [fullScreenContext release];
+    if (actualContext == fullScreenContext) actualContext = nil;
+    fullScreenContext = nil;
+  }
+
+  [super dealloc];
+}
 @end
index 2c144f5..d7fba96 100644 (file)
@@ -65,12 +65,12 @@ struct _GstOSXWindow {
   gboolean internal;
   GstOSXVideoSinkWindow* win;
   GstGLView* gstview;
+  NSAutoreleasePool *pool;
 };
 
 struct _GstOSXVideoSink {
   /* Our element stuff */
   GstVideoSink videosink;
-
   GstOSXWindow *osxwindow;
   
   gint fps_n;
@@ -92,6 +92,16 @@ 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 GstAppDelegate : NSObject
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
+@end
+
 G_END_DECLS
 
 #endif /* __GST_OSX_VIDEO_SINK_H__ */
index 0122c4c..a521d06 100644 (file)
@@ -27,7 +27,7 @@
 
 /* Object header */
 #include "osxvideosink.h"
-
+#include <unistd.h>
 #import "cocoawindow.h"
 
 /* Debugging category */
@@ -51,9 +51,31 @@ GST_STATIC_PAD_TEMPLATE ("sink",
         "framerate = (fraction) [ 0, MAX ], "
         "width = (int) [ 1, MAX ], "
        "height = (int) [ 1, MAX ], "
+#ifdef G_BYTE_ORDER == G_BIG_ENDIAN
+       "format = (fourcc) YUY2")
+#else
         "format = (fourcc) UYVY")
+#endif
     );
 
+// much of the following cocoa NSApp code comes from libsdl and libcaca
+@implementation NSApplication(Gst)
+- (void)setRunning
+{
+    _running = 1;
+}
+@end
+
+@implementation GstAppDelegate : NSObject
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+    // destroy stuff here!
+    GST_DEBUG("Kill me please!");
+    return NSTerminateNow;
+}
+@end
+
+
 enum
 {
   ARG_0,
@@ -64,18 +86,113 @@ enum
 
 static GstVideoSinkClass *parent_class = NULL;
 
+
 /* cocoa event loop - needed if not run in own app */
-/* FIXME : currently disabled since a huge memory leak happens if it is run. */
-gpointer
+gint
 cocoa_event_loop (GstOSXVideoSink * vsink)
 {
-  GST_DEBUG_OBJECT (vsink, "About to start cocoa event loop");
+  NSAutoreleasePool *pool;
+
+  pool = [[NSAutoreleasePool alloc] init];
+
+  if ([NSApp isRunning]) {
+    NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
+                           untilDate:[NSDate distantPast]
+                           inMode:NSDefaultRunLoopMode dequeue:YES ];
+    if ( event != nil ) {
+      switch ([event type]) {
+      default: //XXX Feed me please
+        [NSApp sendEvent:event];
+      break;
+      }
+    }
+  }
+
+  [pool release];
 
-  [NSApp run];
+  return TRUE;
+}
 
-  GST_DEBUG_OBJECT (vsink, "Cocoa event loop ended");
+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;
+}
 
-  return NULL;
+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];
 }
 
 /* This function handles osx window creation */
@@ -84,6 +201,7 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width,
     gint height)
 {
   NSRect rect;
+
   GstOSXWindow *osxwindow = NULL;
 
   g_return_val_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink), NULL);
@@ -93,28 +211,50 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width,
   osxwindow->width = width;
   osxwindow->height = height;
   osxwindow->internal = TRUE;
+  osxwindow->pool = [[NSAutoreleasePool alloc] init];
 
   if (osxvideosink->embed == FALSE) {
-    NSAutoreleasePool *pool;
+    ProcessSerialNumber psn;
+    unsigned int 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;
 
-    pool =[[NSAutoreleasePool alloc] init];
+    if (!GetCurrentProcess(&psn)) {
+        TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+        SetFrontProcess(&psn);
+    }
 
     [NSApplication sharedApplication];
-    osxwindow->win =[[GstOSXVideoSinkWindow alloc] initWithContentRect: rect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask backing: NSBackingStoreBuffered defer: NO screen:nil];
+    osxwindow->win =[[GstOSXVideoSinkWindow alloc]
+                         initWithContentRect: rect
+                         styleMask: mask
+                         backing: NSBackingStoreBuffered
+                         defer: NO
+                         screen: nil];
+    [osxwindow->win autorelease];
+    [NSApplication sharedApplication];
     [osxwindow->win makeKeyAndOrderFront:NSApp];
     osxwindow->gstview =[osxwindow->win gstView];
+    [osxwindow->gstview autorelease];
     if (osxvideosink->fullscreen)
       [osxwindow->gstview setFullScreen:YES];
 
-    [pool release];
+    CreateApplicationMenus();
+
+    [NSApp finishLaunching];
+    [NSApp setDelegate:[[GstAppDelegate alloc] init]];
 
-    /* Start Cocoa event loop */
-//     g_thread_create ((GThreadFunc) cocoa_event_loop, osxvideosink, FALSE, NULL);
+    [NSApp setRunning];
+    // insert event dispatch in the glib main loop
+    g_idle_add ((GSourceFunc) cocoa_event_loop, osxvideosink);
   } else {
     /* Needs to be embedded */
 
@@ -123,6 +263,7 @@ gst_osx_video_sink_osxwindow_new (GstOSXVideoSink * osxvideosink, gint width,
     rect.size.width = (float) osxwindow->width;
     rect.size.height = (float) osxwindow->height;
     osxwindow->gstview =[[GstGLView alloc] initWithFrame:rect];
+    [osxwindow->gstview autorelease];
     /* send signal 
        FIXME: need to send a bus message */
     /*g_signal_emit (G_OBJECT(osxvideosink),
@@ -140,6 +281,8 @@ gst_osx_video_sink_osxwindow_destroy (GstOSXVideoSink * osxvideosink,
   g_return_if_fail (osxwindow != NULL);
   g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
 
+  [osxwindow->pool release];
+
   g_free (osxwindow);
 }
 
@@ -149,7 +292,7 @@ gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
     GstOSXWindow * osxwindow, guint width, guint height)
 {
   NSSize size;
-
+  NSAutoreleasePool *subPool = [[NSAutoreleasePool alloc] init];
   g_return_if_fail (osxwindow != NULL);
   g_return_if_fail (GST_IS_OSX_VIDEO_SINK (osxvideosink));
 
@@ -159,7 +302,8 @@ gst_osx_video_sink_osxwindow_resize (GstOSXVideoSink * osxvideosink,
   size.width = width;
   size.height = height;
   /* Call relevant cocoa function to resize window */
-[osxwindow->win setContentSize:size];
+ [osxwindow->win setContentSize:size];
+ [subPool release];
 }
 
 static void
@@ -350,10 +494,8 @@ static void
 gst_osx_video_sink_init (GstOSXVideoSink * osxvideosink)
 {
 
-
   osxvideosink->osxwindow = NULL;
 
-
   osxvideosink->fps_n = 0;
   osxvideosink->fps_d = 0;