2 * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
20 * The development of this code was made possible due to the involvement of Pioneers
21 * of the Inevitable, the creators of the Songbird Music player
25 /* inspiration gained from looking at source of osx video out of xine and vlc
26 * and is reflected in the code
30 #include <Cocoa/Cocoa.h>
32 #import "cocoawindow.h"
33 #import "osxvideosink.h"
35 #include <OpenGL/OpenGL.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
39 #include <Carbon/Carbon.h>
41 /* Debugging category */
42 #include <gst/gstinfo.h>
45 const gchar* gst_keycode_to_keyname(gint16 keycode)
107 case kVK_ANSI_RightBracket:
108 return "bracketright";
113 case kVK_ANSI_LeftBracket:
114 return "bracketleft";
127 case kVK_ANSI_Semicolon:
129 case kVK_ANSI_Backslash:
139 case kVK_ANSI_Period:
143 case kVK_ANSI_KeypadDecimal:
145 case kVK_ANSI_KeypadMultiply:
146 return "KP_Multiply";
147 case kVK_ANSI_KeypadPlus:
149 case kVK_ANSI_KeypadClear:
151 case kVK_ANSI_KeypadDivide:
153 case kVK_ANSI_KeypadEnter:
155 case kVK_ANSI_KeypadMinus:
156 return "KP_Subtract";
157 case kVK_ANSI_KeypadEquals:
159 case kVK_ANSI_Keypad0:
161 case kVK_ANSI_Keypad1:
163 case kVK_ANSI_Keypad2:
165 case kVK_ANSI_Keypad3:
167 case kVK_ANSI_Keypad4:
169 case kVK_ANSI_Keypad5:
171 case kVK_ANSI_Keypad6:
173 case kVK_ANSI_Keypad7:
175 case kVK_ANSI_Keypad8:
177 case kVK_ANSI_Keypad9:
180 /* keycodes for keys that are independent of keyboard layout*/
204 case kVK_RightOption:
206 case kVK_RightControl:
256 case kVK_ForwardDelete:
281 @ implementation GstOSXVideoSinkWindow
283 /* The object has to be released */
284 - (id) initWithContentNSRect: (NSRect) rect
285 styleMask: (unsigned int) styleMask
286 backing: (NSBackingStoreType) bufferingType
288 screen:(NSScreen *) aScreen
290 self = [super initWithContentRect: rect
292 backing: bufferingType
296 GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
298 gstview = [[GstGLView alloc] initWithFrame:rect];
301 [self setContentView:gstview];
302 [self setTitle:@"GStreamer Video Output"];
307 - (void) setContentSize:(NSSize) size {
309 height = size.height;
311 [super setContentSize:size];
314 - (GstGLView *) gstView {
318 - (void) awakeFromNib {
319 [self setAcceptsMouseMovedEvents:YES];
326 // OpenGL implementation
329 @ implementation GstGLView
331 - (id) initWithFrame:(NSRect) frame {
332 NSOpenGLPixelFormat *fmt;
333 NSOpenGLPixelFormatAttribute attribs[] = {
334 NSOpenGLPFANoRecovery,
335 NSOpenGLPFADoubleBuffer,
336 NSOpenGLPFAColorSize, 24,
337 NSOpenGLPFAAlphaSize, 8,
338 NSOpenGLPFADepthSize, 24,
339 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1090
345 fmt = [[NSOpenGLPixelFormat alloc]
346 initWithAttributes:attribs];
349 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
353 self = [super initWithFrame: frame pixelFormat:fmt];
356 actualContext = [self openGLContext];
357 [actualContext makeCurrentContext];
358 [actualContext update];
360 /* Black background */
361 glClearColor (0.0, 0.0, 0.0, 0.0);
365 width = frame.size.width;
366 height = frame.size.height;
367 drawingBounds = NSMakeRect(0, 0, width, height);
369 GST_LOG ("Width: %d Height: %d", width, height);
371 trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
372 options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
376 [self addTrackingArea:trackingArea];
377 mainThread = [NSThread mainThread];
383 - (NSRect) getDrawingBounds {
384 return drawingBounds;
389 gdouble frame_par, view_par;
390 gint view_height, view_width, c_height, c_width, c_x, c_y;
393 GST_LOG ("reshaping");
399 [actualContext makeCurrentContext];
401 bounds = [self bounds];
402 view_width = bounds.size.width;
403 view_height = bounds.size.height;
405 frame_par = (gdouble) width / height;
406 view_par = (gdouble) view_width / view_height;
407 if (!keepAspectRatio)
408 view_par = frame_par;
410 if (frame_par == view_par) {
411 c_height = view_height;
412 c_width = view_width;
415 } else if (frame_par < view_par) {
416 c_height = view_height;
417 c_width = c_height * frame_par;
418 c_x = (view_width - c_width) / 2;
421 c_width = view_width;
422 c_height = c_width / frame_par;
424 c_y = (view_height - c_height) / 2;
427 drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
428 glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
431 - (void) initTextures {
433 [actualContext makeCurrentContext];
435 /* Free previous texture if any */
437 glDeleteTextures (1, (GLuint *)&pi_texture);
441 data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
443 data = g_malloc0(width * height * sizeof(short));
445 /* Create textures */
446 glGenTextures (1, (GLuint *)&pi_texture);
448 glEnable (GL_TEXTURE_RECTANGLE_EXT);
449 glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
451 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
452 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
454 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
456 /* Use VRAM texturing */
457 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
458 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
460 /* Tell the driver not to make a copy of the texture but to use
462 glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
464 /* Linear interpolation */
465 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
466 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
468 /* I have no idea what this exactly does, but it seems to be
469 necessary for scaling */
470 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
471 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
472 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
473 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
474 // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
476 glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
478 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
484 - (void) reloadTexture {
489 GST_LOG ("Reloading Texture");
491 [actualContext makeCurrentContext];
493 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
494 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
496 /* glTexSubImage2D is faster than glTexImage2D
497 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
498 TextureRange/MainOpenGLView.m.htm */
499 glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
501 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
514 glTexCoord2f (0.0, 0.0);
515 glVertex2f (-f_x, f_y);
517 glTexCoord2f (0.0, (float) height);
518 glVertex2f (-f_x, -f_y);
520 glTexCoord2f ((float) width, (float) height);
521 glVertex2f (f_x, -f_y);
523 glTexCoord2f ((float) width, 0.0);
524 glVertex2f (f_x, f_y);
528 - (void) drawRect:(NSRect) rect {
529 GLint params[] = { 1 };
531 [actualContext makeCurrentContext];
533 CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
535 /* Black background */
536 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
539 [actualContext flushBuffer];
544 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
547 [actualContext flushBuffer];
550 - (void) displayTexture {
551 if ([self lockFocusIfCanDraw]) {
553 [self drawRect:[self bounds]];
554 [self reloadTexture];
562 - (char *) getTextureBuffer {
566 - (void) setFullScreen:(BOOL) flag {
567 if (!fullscreen && flag) {
569 /* Create the new pixel format */
570 NSOpenGLPixelFormat *fmt;
571 NSOpenGLPixelFormatAttribute attribs[] = {
572 NSOpenGLPFAAccelerated,
573 NSOpenGLPFANoRecovery,
574 NSOpenGLPFADoubleBuffer,
575 NSOpenGLPFAColorSize, 24,
576 NSOpenGLPFAAlphaSize, 8,
577 NSOpenGLPFADepthSize, 24,
578 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
579 NSOpenGLPFAFullScreen,
581 NSOpenGLPFAScreenMask,
582 CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
586 fmt = [[NSOpenGLPixelFormat alloc]
587 initWithAttributes:attribs];
590 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
594 /* Create the new OpenGL context */
595 fullScreenContext = [[NSOpenGLContext alloc]
596 initWithFormat: fmt shareContext:nil];
597 if (!fullScreenContext) {
598 GST_WARNING ("Failed to create new NSOpenGLContext");
602 actualContext = fullScreenContext;
604 /* Capture display, switch to fullscreen */
605 if (CGCaptureAllDisplays () != CGDisplayNoErr) {
606 GST_WARNING ("CGCaptureAllDisplays() failed");
609 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
610 [fullScreenContext setFullScreen];
612 [fullScreenContext makeCurrentContext];
617 [self setNeedsDisplay:YES];
619 } else if (fullscreen && !flag) {
620 // fullscreen now and needs to go back to normal
623 actualContext = [self openGLContext];
625 [NSOpenGLContext clearCurrentContext];
626 [fullScreenContext clearDrawable];
627 [fullScreenContext release];
628 fullScreenContext = nil;
630 CGReleaseAllDisplays ();
635 [self setNeedsDisplay:YES];
642 - (void) setVideoSize: (int)w : (int)h {
643 GST_LOG ("width:%d, height:%d", w, h);
652 - (void) setKeepAspectRatio: (BOOL) flag {
653 keepAspectRatio = flag;
657 #ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
658 - (void) setMainThread: (NSThread *) thread {
663 - (void) haveSuperviewReal:(NSMutableArray *)closure {
664 BOOL haveSuperview = [self superview] != nil;
665 [closure addObject:[NSNumber numberWithBool:haveSuperview]];
668 - (BOOL) haveSuperview {
669 NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
670 [self performSelector:@selector(haveSuperviewReal:)
672 withObject:(id)closure waitUntilDone:YES];
674 return [[closure objectAtIndex:0] boolValue];
677 - (void) addToSuperviewReal:(NSView *)superview {
679 [superview addSubview:self];
680 bounds = [superview bounds];
681 [self setFrame:bounds];
682 [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
685 - (void) addToSuperview: (NSView *)superview {
686 [self performSelector:@selector(addToSuperviewReal:)
688 withObject:superview waitUntilDone:YES];
691 - (void) removeFromSuperview: (id)unused
693 [self removeFromSuperview];
697 GST_LOG ("dealloc called");
698 if (data) g_free(data);
700 if (fullScreenContext) {
701 [NSOpenGLContext clearCurrentContext];
702 [fullScreenContext clearDrawable];
703 [fullScreenContext release];
704 if (actualContext == fullScreenContext) actualContext = nil;
705 fullScreenContext = nil;
711 - (void)updateTrackingAreas {
712 [self removeTrackingArea:trackingArea];
713 [trackingArea release];
714 trackingArea = [[NSTrackingArea alloc] initWithRect: [self bounds]
715 options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
716 owner:self userInfo:nil];
717 [self addTrackingArea:trackingArea];
720 - (BOOL)acceptsFirstResponder {
724 - (void) setNavigation:(GstNavigation *)nav
729 - (void)sendMouseEvent:(NSEvent *)event : (const char *)event_name
738 switch ([event type]) {
742 case NSLeftMouseDown:
746 case NSRightMouseDown:
755 location = [self convertPoint:[event locationInWindow] fromView:nil];
761 y = (1 - ((gdouble) y) / [self bounds].size.height) * [self bounds].size.height;
763 gst_navigation_send_mouse_event (navigation, event_name, button, x, y);
766 - (void)sendKeyEvent:(NSEvent *)event : (const char *)event_name
771 gst_navigation_send_key_event(navigation, event_name, gst_keycode_to_keyname([event keyCode]));
774 - (void)sendModifierKeyEvent:(NSEvent *)event
776 NSUInteger flags = [event modifierFlags];
777 const gchar* event_name = flags > savedModifierFlags ? "key-press" : "key-release";
778 savedModifierFlags = flags;
779 [self sendKeyEvent: event: event_name];
782 - (void)keyDown:(NSEvent *) event;
784 [self sendKeyEvent: event: "key-press"];
785 [super keyDown: event];
788 - (void)keyUp:(NSEvent *) event;
790 [self sendKeyEvent: event: "key-release"];
791 [super keyUp: event];
794 - (void)flagsChanged:(NSEvent *) event;
796 [self sendModifierKeyEvent: event];
797 [super flagsChanged: event];
800 - (void)mouseDown:(NSEvent *) event;
802 [self sendMouseEvent:event: "mouse-button-press"];
803 [super mouseDown: event];
806 - (void)mouseUp:(NSEvent *) event;
808 [self sendMouseEvent:event: "mouse-button-release"];
809 [super mouseUp: event];
812 - (void)mouseMoved:(NSEvent *)event;
814 [self sendMouseEvent:event: "mouse-move"];
815 [super mouseMoved: event];
818 - (void)mouseEntered:(NSEvent *)event;
820 [super mouseEntered: event];
823 - (void)mouseExited:(NSEvent *)event;
825 [super mouseExited: event];