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,
343 fmt = [[NSOpenGLPixelFormat alloc]
344 initWithAttributes:attribs];
347 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
351 self = [super initWithFrame: frame pixelFormat:fmt];
354 actualContext = [self openGLContext];
355 [actualContext makeCurrentContext];
356 [actualContext update];
358 /* Black background */
359 glClearColor (0.0, 0.0, 0.0, 0.0);
363 width = frame.size.width;
364 height = frame.size.height;
365 drawingBounds = NSMakeRect(0, 0, width, height);
367 GST_LOG ("Width: %d Height: %d", width, height);
369 trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
370 options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
374 [self addTrackingArea:trackingArea];
375 mainThread = [NSThread mainThread];
381 - (NSRect) getDrawingBounds {
382 return drawingBounds;
387 gdouble frame_par, view_par;
388 gint view_height, view_width, c_height, c_width, c_x, c_y;
391 GST_LOG ("reshaping");
397 [actualContext makeCurrentContext];
399 bounds = [self bounds];
400 view_width = bounds.size.width;
401 view_height = bounds.size.height;
403 frame_par = (gdouble) width / height;
404 view_par = (gdouble) view_width / view_height;
405 if (!keepAspectRatio)
406 view_par = frame_par;
408 if (frame_par == view_par) {
409 c_height = view_height;
410 c_width = view_width;
413 } else if (frame_par < view_par) {
414 c_height = view_height;
415 c_width = c_height * frame_par;
416 c_x = (view_width - c_width) / 2;
419 c_width = view_width;
420 c_height = c_width / frame_par;
422 c_y = (view_height - c_height) / 2;
425 drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
426 glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
429 - (void) initTextures {
431 [actualContext makeCurrentContext];
433 /* Free previous texture if any */
435 glDeleteTextures (1, (GLuint *)&pi_texture);
439 data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
441 data = g_malloc0(width * height * sizeof(short));
443 /* Create textures */
444 glGenTextures (1, (GLuint *)&pi_texture);
446 glEnable (GL_TEXTURE_RECTANGLE_EXT);
447 glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
449 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
450 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
452 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
454 /* Use VRAM texturing */
455 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
456 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
458 /* Tell the driver not to make a copy of the texture but to use
460 glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
462 /* Linear interpolation */
463 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
464 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
466 /* I have no idea what this exactly does, but it seems to be
467 necessary for scaling */
468 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
469 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
470 glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
471 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
472 // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
474 glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
476 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
482 - (void) reloadTexture {
487 GST_LOG ("Reloading Texture");
489 [actualContext makeCurrentContext];
491 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
492 glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
494 /* glTexSubImage2D is faster than glTexImage2D
495 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
496 TextureRange/MainOpenGLView.m.htm */
497 glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
499 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data); //FIXME
512 glTexCoord2f (0.0, 0.0);
513 glVertex2f (-f_x, f_y);
515 glTexCoord2f (0.0, (float) height);
516 glVertex2f (-f_x, -f_y);
518 glTexCoord2f ((float) width, (float) height);
519 glVertex2f (f_x, -f_y);
521 glTexCoord2f ((float) width, 0.0);
522 glVertex2f (f_x, f_y);
526 - (void) drawRect:(NSRect) rect {
527 GLint params[] = { 1 };
529 [actualContext makeCurrentContext];
531 CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
533 /* Black background */
534 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
537 [actualContext flushBuffer];
542 glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
545 [actualContext flushBuffer];
548 - (void) displayTexture {
549 if ([self lockFocusIfCanDraw]) {
551 [self drawRect:[self bounds]];
552 [self reloadTexture];
560 - (char *) getTextureBuffer {
564 - (void) setFullScreen:(BOOL) flag {
565 if (!fullscreen && flag) {
567 /* Create the new pixel format */
568 NSOpenGLPixelFormat *fmt;
569 NSOpenGLPixelFormatAttribute attribs[] = {
570 NSOpenGLPFAAccelerated,
571 NSOpenGLPFANoRecovery,
572 NSOpenGLPFADoubleBuffer,
573 NSOpenGLPFAColorSize, 24,
574 NSOpenGLPFAAlphaSize, 8,
575 NSOpenGLPFADepthSize, 24,
576 NSOpenGLPFAFullScreen,
577 NSOpenGLPFAScreenMask,
578 CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
582 fmt = [[NSOpenGLPixelFormat alloc]
583 initWithAttributes:attribs];
586 GST_WARNING ("Cannot create NSOpenGLPixelFormat");
590 /* Create the new OpenGL context */
591 fullScreenContext = [[NSOpenGLContext alloc]
592 initWithFormat: fmt shareContext:nil];
593 if (!fullScreenContext) {
594 GST_WARNING ("Failed to create new NSOpenGLContext");
598 actualContext = fullScreenContext;
600 /* Capture display, switch to fullscreen */
601 if (CGCaptureAllDisplays () != CGDisplayNoErr) {
602 GST_WARNING ("CGCaptureAllDisplays() failed");
605 [fullScreenContext setFullScreen];
606 [fullScreenContext makeCurrentContext];
611 [self setNeedsDisplay:YES];
613 } else if (fullscreen && !flag) {
614 // fullscreen now and needs to go back to normal
617 actualContext = [self openGLContext];
619 [NSOpenGLContext clearCurrentContext];
620 [fullScreenContext clearDrawable];
621 [fullScreenContext release];
622 fullScreenContext = nil;
624 CGReleaseAllDisplays ();
629 [self setNeedsDisplay:YES];
636 - (void) setVideoSize: (int)w : (int)h {
637 GST_LOG ("width:%d, height:%d", w, h);
646 - (void) setKeepAspectRatio: (BOOL) flag {
647 keepAspectRatio = flag;
651 - (void) setMainThread: (NSThread *) thread {
655 - (void) haveSuperviewReal:(NSMutableArray *)closure {
656 BOOL haveSuperview = [self superview] != nil;
657 [closure addObject:[NSNumber numberWithBool:haveSuperview]];
660 - (BOOL) haveSuperview {
661 NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
662 [self performSelector:@selector(haveSuperviewReal:)
664 withObject:(id)closure waitUntilDone:YES];
666 return [[closure objectAtIndex:0] boolValue];
669 - (void) addToSuperviewReal:(NSView *)superview {
671 [superview addSubview:self];
672 bounds = [superview bounds];
673 [self setFrame:bounds];
674 [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
677 - (void) addToSuperview: (NSView *)superview {
678 [self performSelector:@selector(addToSuperviewReal:)
680 withObject:superview waitUntilDone:YES];
683 - (void) removeFromSuperview: (id)unused
685 [self removeFromSuperview];
689 GST_LOG ("dealloc called");
690 if (data) g_free(data);
692 if (fullScreenContext) {
693 [NSOpenGLContext clearCurrentContext];
694 [fullScreenContext clearDrawable];
695 [fullScreenContext release];
696 if (actualContext == fullScreenContext) actualContext = nil;
697 fullScreenContext = nil;
703 - (void)updateTrackingAreas {
704 [self removeTrackingArea:trackingArea];
705 [trackingArea release];
706 trackingArea = [[NSTrackingArea alloc] initWithRect: [self bounds]
707 options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
708 owner:self userInfo:nil];
709 [self addTrackingArea:trackingArea];
712 - (BOOL)acceptsFirstResponder {
716 - (void) setNavigation:(GstNavigation *)nav
721 - (void)sendMouseEvent:(NSEvent *)event : (const char *)event_name
730 switch ([event type]) {
734 case NSLeftMouseDown:
738 case NSRightMouseDown:
747 location = [self convertPoint:[event locationInWindow] fromView:nil];
753 y = (1 - ((gdouble) y) / [self bounds].size.height) * [self bounds].size.height;
755 gst_navigation_send_mouse_event (navigation, event_name, button, x, y);
758 - (void)sendKeyEvent:(NSEvent *)event : (const char *)event_name
763 gst_navigation_send_key_event(navigation, event_name, gst_keycode_to_keyname([event keyCode]));
766 - (void)sendModifierKeyEvent:(NSEvent *)event
768 NSUInteger flags = [event modifierFlags];
769 const gchar* event_name = flags > savedModifierFlags ? "key-press" : "key-release";
770 savedModifierFlags = flags;
771 [self sendKeyEvent: event: event_name];
774 - (void)keyDown:(NSEvent *) event;
776 [self sendKeyEvent: event: "key-press"];
777 [super keyDown: event];
780 - (void)keyUp:(NSEvent *) event;
782 [self sendKeyEvent: event: "key-release"];
783 [super keyUp: event];
786 - (void)flagsChanged:(NSEvent *) event;
788 [self sendModifierKeyEvent: event];
789 [super flagsChanged: event];
792 - (void)mouseDown:(NSEvent *) event;
794 [self sendMouseEvent:event: "mouse-button-press"];
795 [super mouseDown: event];
798 - (void)mouseUp:(NSEvent *) event;
800 [self sendMouseEvent:event: "mouse-button-release"];
801 [super mouseUp: event];
804 - (void)mouseMoved:(NSEvent *)event;
806 [self sendMouseEvent:event: "mouse-move"];
807 [super mouseMoved: event];
810 - (void)mouseEntered:(NSEvent *)event;
812 [super mouseEntered: event];
815 - (void)mouseExited:(NSEvent *)event;
817 [super mouseExited: event];