mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
81e15d22c0
The hack causes deadlocks and other interesting problems and it really can only be fixed properly inside GLib. We will include a patch for GLib in our builds for now that handles this, and hopefully at some point GLib will also merge a proper solution. A proper solution would first require to refactor the polling in GMainContext to only provide a single fd, e.g. via epoll/kqueue or a thread like the one added by our patch. Then this single fd could be retrieved from the GMainContext and directly integrated into a NSRunLoop. https://bugzilla.gnome.org/show_bug.cgi?id=741450 https://bugzilla.gnome.org/show_bug.cgi?id=704374
822 lines
19 KiB
Objective-C
822 lines
19 KiB
Objective-C
/* GStreamer
|
|
* Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
|
|
* Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
* The development of this code was made possible due to the involvement of Pioneers
|
|
* of the Inevitable, the creators of the Songbird Music player
|
|
*
|
|
*/
|
|
|
|
/* inspiration gained from looking at source of osx video out of xine and vlc
|
|
* and is reflected in the code
|
|
*/
|
|
|
|
|
|
#include <Cocoa/Cocoa.h>
|
|
#include <gst/gst.h>
|
|
#import "cocoawindow.h"
|
|
#import "osxvideosink.h"
|
|
|
|
#include <OpenGL/OpenGL.h>
|
|
#include <OpenGL/gl.h>
|
|
#include <OpenGL/glext.h>
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
/* Debugging category */
|
|
#include <gst/gstinfo.h>
|
|
|
|
static
|
|
const gchar* gst_keycode_to_keyname(gint16 keycode)
|
|
{
|
|
switch (keycode)
|
|
{
|
|
case kVK_ANSI_A:
|
|
return "a";
|
|
case kVK_ANSI_S:
|
|
return "s";
|
|
case kVK_ANSI_D:
|
|
return "d";
|
|
case kVK_ANSI_F:
|
|
return "f";
|
|
case kVK_ANSI_H:
|
|
return "h";
|
|
case kVK_ANSI_G:
|
|
return "g";
|
|
case kVK_ANSI_Z:
|
|
return "z";
|
|
case kVK_ANSI_X:
|
|
return "x";
|
|
case kVK_ANSI_C:
|
|
return "c";
|
|
case kVK_ANSI_V:
|
|
return "v";
|
|
case kVK_ANSI_B:
|
|
return "b";
|
|
case kVK_ANSI_Q:
|
|
return "q";
|
|
case kVK_ANSI_W:
|
|
return "w";
|
|
case kVK_ANSI_E:
|
|
return "e";
|
|
case kVK_ANSI_R:
|
|
return "r";
|
|
case kVK_ANSI_Y:
|
|
return "y";
|
|
case kVK_ANSI_T:
|
|
return "t";
|
|
case kVK_ANSI_1:
|
|
return "1";
|
|
case kVK_ANSI_2:
|
|
return "2";
|
|
case kVK_ANSI_3:
|
|
return "3";
|
|
case kVK_ANSI_4:
|
|
return "4";
|
|
case kVK_ANSI_6:
|
|
return "6";
|
|
case kVK_ANSI_5:
|
|
return "5";
|
|
case kVK_ANSI_Equal:
|
|
return "equal";
|
|
case kVK_ANSI_9:
|
|
return "9";
|
|
case kVK_ANSI_7:
|
|
return "7";
|
|
case kVK_ANSI_Minus:
|
|
return "minus";
|
|
case kVK_ANSI_8:
|
|
return "8";
|
|
case kVK_ANSI_0:
|
|
return "0";
|
|
case kVK_ANSI_RightBracket:
|
|
return "bracketright";
|
|
case kVK_ANSI_O:
|
|
return "0";
|
|
case kVK_ANSI_U:
|
|
return "u";
|
|
case kVK_ANSI_LeftBracket:
|
|
return "bracketleft";
|
|
case kVK_ANSI_I:
|
|
return "i";
|
|
case kVK_ANSI_P:
|
|
return "p";
|
|
case kVK_ANSI_L:
|
|
return "l";
|
|
case kVK_ANSI_J:
|
|
return "j";
|
|
case kVK_ANSI_Quote:
|
|
return "apostrophe";
|
|
case kVK_ANSI_K:
|
|
return "k";
|
|
case kVK_ANSI_Semicolon:
|
|
return "semicolon";
|
|
case kVK_ANSI_Backslash:
|
|
return "backslash";
|
|
case kVK_ANSI_Comma:
|
|
return "comma";
|
|
case kVK_ANSI_Slash:
|
|
return "slash";
|
|
case kVK_ANSI_N:
|
|
return "n";
|
|
case kVK_ANSI_M:
|
|
return "m";
|
|
case kVK_ANSI_Period:
|
|
return "period";
|
|
case kVK_ANSI_Grave:
|
|
return "grave";
|
|
case kVK_ANSI_KeypadDecimal:
|
|
return "KP_Delete";
|
|
case kVK_ANSI_KeypadMultiply:
|
|
return "KP_Multiply";
|
|
case kVK_ANSI_KeypadPlus:
|
|
return "KP_Add";
|
|
case kVK_ANSI_KeypadClear:
|
|
return "KP_Clear";
|
|
case kVK_ANSI_KeypadDivide:
|
|
return "KP_Divide";
|
|
case kVK_ANSI_KeypadEnter:
|
|
return "KP_Enter";
|
|
case kVK_ANSI_KeypadMinus:
|
|
return "KP_Subtract";
|
|
case kVK_ANSI_KeypadEquals:
|
|
return "KP_Equals";
|
|
case kVK_ANSI_Keypad0:
|
|
return "KP_Insert";
|
|
case kVK_ANSI_Keypad1:
|
|
return "KP_End";
|
|
case kVK_ANSI_Keypad2:
|
|
return "KP_Down";
|
|
case kVK_ANSI_Keypad3:
|
|
return "KP_Next";
|
|
case kVK_ANSI_Keypad4:
|
|
return "KP_Left";
|
|
case kVK_ANSI_Keypad5:
|
|
return "KP_Begin";
|
|
case kVK_ANSI_Keypad6:
|
|
return "KP_Right";
|
|
case kVK_ANSI_Keypad7:
|
|
return "KP_Home";
|
|
case kVK_ANSI_Keypad8:
|
|
return "KP_Up";
|
|
case kVK_ANSI_Keypad9:
|
|
return "KP_Prior";
|
|
|
|
/* keycodes for keys that are independent of keyboard layout*/
|
|
|
|
case kVK_Return:
|
|
return "Return";
|
|
case kVK_Tab:
|
|
return "Tab";
|
|
case kVK_Space:
|
|
return "space";
|
|
case kVK_Delete:
|
|
return "Backspace";
|
|
case kVK_Escape:
|
|
return "Escape";
|
|
case kVK_Command:
|
|
return "Command";
|
|
case kVK_Shift:
|
|
return "Shift_L";
|
|
case kVK_CapsLock:
|
|
return "Caps_Lock";
|
|
case kVK_Option:
|
|
return "Option_L";
|
|
case kVK_Control:
|
|
return "Control_L";
|
|
case kVK_RightShift:
|
|
return "Shift_R";
|
|
case kVK_RightOption:
|
|
return "Option_R";
|
|
case kVK_RightControl:
|
|
return "Control_R";
|
|
case kVK_Function:
|
|
return "Function";
|
|
case kVK_F17:
|
|
return "F17";
|
|
case kVK_VolumeUp:
|
|
return "VolumeUp";
|
|
case kVK_VolumeDown:
|
|
return "VolumeDown";
|
|
case kVK_Mute:
|
|
return "Mute";
|
|
case kVK_F18:
|
|
return "F18";
|
|
case kVK_F19:
|
|
return "F19";
|
|
case kVK_F20:
|
|
return "F20";
|
|
case kVK_F5:
|
|
return "F5";
|
|
case kVK_F6:
|
|
return "F6";
|
|
case kVK_F7:
|
|
return "F7";
|
|
case kVK_F3:
|
|
return "F3";
|
|
case kVK_F8:
|
|
return "F8";
|
|
case kVK_F9:
|
|
return "F9";
|
|
case kVK_F11:
|
|
return "F11";
|
|
case kVK_F13:
|
|
return "F13";
|
|
case kVK_F16:
|
|
return "F16";
|
|
case kVK_F14:
|
|
return "F14";
|
|
case kVK_F10:
|
|
return "F10";
|
|
case kVK_F12:
|
|
return "F12";
|
|
case kVK_F15:
|
|
return "F15";
|
|
case kVK_Help:
|
|
return "Help";
|
|
case kVK_Home:
|
|
return "Home";
|
|
case kVK_PageUp:
|
|
return "Prior";
|
|
case kVK_ForwardDelete:
|
|
return "Delete";
|
|
case kVK_F4:
|
|
return "F4";
|
|
case kVK_End:
|
|
return "End";
|
|
case kVK_F2:
|
|
return "F2";
|
|
case kVK_PageDown:
|
|
return "Next";
|
|
case kVK_F1:
|
|
return "F1";
|
|
case kVK_LeftArrow:
|
|
return "Left";
|
|
case kVK_RightArrow:
|
|
return "Right";
|
|
case kVK_DownArrow:
|
|
return "Down";
|
|
case kVK_UpArrow:
|
|
return "Up";
|
|
default:
|
|
return "";
|
|
};
|
|
}
|
|
|
|
@ implementation GstOSXVideoSinkWindow
|
|
|
|
/* The object has to be released */
|
|
- (id) initWithContentNSRect: (NSRect) rect
|
|
styleMask: (unsigned int) styleMask
|
|
backing: (NSBackingStoreType) bufferingType
|
|
defer: (BOOL) flag
|
|
screen:(NSScreen *) aScreen
|
|
{
|
|
self = [super initWithContentRect: rect
|
|
styleMask: styleMask
|
|
backing: bufferingType
|
|
defer: flag
|
|
screen:aScreen];
|
|
|
|
GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
|
|
|
|
gstview = [[GstGLView alloc] initWithFrame:rect];
|
|
|
|
if (gstview)
|
|
[self setContentView:gstview];
|
|
[self setTitle:@"GStreamer Video Output"];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) setContentSize:(NSSize) size {
|
|
width = size.width;
|
|
height = size.height;
|
|
|
|
[super setContentSize:size];
|
|
}
|
|
|
|
- (GstGLView *) gstView {
|
|
return gstview;
|
|
}
|
|
|
|
- (void) awakeFromNib {
|
|
[self setAcceptsMouseMovedEvents:YES];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
//
|
|
// OpenGL implementation
|
|
//
|
|
|
|
@ implementation GstGLView
|
|
|
|
- (id) initWithFrame:(NSRect) frame {
|
|
NSOpenGLPixelFormat *fmt;
|
|
NSOpenGLPixelFormatAttribute attribs[] = {
|
|
NSOpenGLPFANoRecovery,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 24,
|
|
NSOpenGLPFAWindow,
|
|
0
|
|
};
|
|
|
|
fmt = [[NSOpenGLPixelFormat alloc]
|
|
initWithAttributes:attribs];
|
|
|
|
if (!fmt) {
|
|
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
|
|
return nil;
|
|
}
|
|
|
|
self = [super initWithFrame: frame pixelFormat:fmt];
|
|
[fmt release];
|
|
|
|
actualContext = [self openGLContext];
|
|
[actualContext makeCurrentContext];
|
|
[actualContext update];
|
|
|
|
/* Black background */
|
|
glClearColor (0.0, 0.0, 0.0, 0.0);
|
|
|
|
pi_texture = 0;
|
|
data = nil;
|
|
width = frame.size.width;
|
|
height = frame.size.height;
|
|
drawingBounds = NSMakeRect(0, 0, width, height);
|
|
|
|
GST_LOG ("Width: %d Height: %d", width, height);
|
|
|
|
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
|
|
options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
|
|
owner:self
|
|
userInfo:nil];
|
|
|
|
[self addTrackingArea:trackingArea];
|
|
mainThread = [NSThread mainThread];
|
|
|
|
[self initTextures];
|
|
return self;
|
|
}
|
|
|
|
- (NSRect) getDrawingBounds {
|
|
return drawingBounds;
|
|
}
|
|
|
|
- (void) reshape {
|
|
NSRect bounds;
|
|
gdouble frame_par, view_par;
|
|
gint view_height, view_width, c_height, c_width, c_x, c_y;
|
|
|
|
|
|
GST_LOG ("reshaping");
|
|
|
|
if (!initDone) {
|
|
return;
|
|
}
|
|
|
|
[actualContext makeCurrentContext];
|
|
|
|
bounds = [self bounds];
|
|
view_width = bounds.size.width;
|
|
view_height = bounds.size.height;
|
|
|
|
frame_par = (gdouble) width / height;
|
|
view_par = (gdouble) view_width / view_height;
|
|
if (!keepAspectRatio)
|
|
view_par = frame_par;
|
|
|
|
if (frame_par == view_par) {
|
|
c_height = view_height;
|
|
c_width = view_width;
|
|
c_x = 0;
|
|
c_y = 0;
|
|
} else if (frame_par < view_par) {
|
|
c_height = view_height;
|
|
c_width = c_height * frame_par;
|
|
c_x = (view_width - c_width) / 2;
|
|
c_y = 0;
|
|
} else {
|
|
c_width = view_width;
|
|
c_height = c_width / frame_par;
|
|
c_x = 0;
|
|
c_y = (view_height - c_height) / 2;
|
|
}
|
|
|
|
drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
|
|
glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
|
|
}
|
|
|
|
- (void) initTextures {
|
|
|
|
[actualContext makeCurrentContext];
|
|
|
|
/* Free previous texture if any */
|
|
if (pi_texture) {
|
|
glDeleteTextures (1, (GLuint *)&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, (GLuint *)&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 */
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
|
|
GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
|
|
|
|
/* Tell the driver not to make a copy of the texture but to use
|
|
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);
|
|
|
|
/* I have no idea what this exactly does, but it seems to be
|
|
necessary for scaling */
|
|
glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
|
|
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); WHY ??
|
|
|
|
glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
|
|
width, height, 0,
|
|
GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
|
|
|
|
|
|
initDone = 1;
|
|
}
|
|
|
|
- (void) reloadTexture {
|
|
if (!initDone) {
|
|
return;
|
|
}
|
|
|
|
GST_LOG ("Reloading Texture");
|
|
|
|
[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
|
|
}
|
|
|
|
- (void) cleanUp {
|
|
initDone = 0;
|
|
}
|
|
|
|
- (void) drawQuad {
|
|
f_x = 1.0;
|
|
f_y = 1.0;
|
|
|
|
glBegin (GL_QUADS);
|
|
/* Top left */
|
|
glTexCoord2f (0.0, 0.0);
|
|
glVertex2f (-f_x, f_y);
|
|
/* Bottom left */
|
|
glTexCoord2f (0.0, (float) height);
|
|
glVertex2f (-f_x, -f_y);
|
|
/* Bottom right */
|
|
glTexCoord2f ((float) width, (float) height);
|
|
glVertex2f (f_x, -f_y);
|
|
/* Top right */
|
|
glTexCoord2f ((float) width, 0.0);
|
|
glVertex2f (f_x, f_y);
|
|
glEnd ();
|
|
}
|
|
|
|
- (void) drawRect:(NSRect) rect {
|
|
GLint params[] = { 1 };
|
|
|
|
[actualContext makeCurrentContext];
|
|
|
|
CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
|
|
|
|
/* Black background */
|
|
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
if (!initDone) {
|
|
[actualContext flushBuffer];
|
|
return;
|
|
}
|
|
|
|
/* Draw */
|
|
glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
|
|
[self drawQuad];
|
|
/* Draw */
|
|
[actualContext flushBuffer];
|
|
}
|
|
|
|
- (void) displayTexture {
|
|
if ([self lockFocusIfCanDraw]) {
|
|
|
|
[self drawRect:[self bounds]];
|
|
[self reloadTexture];
|
|
|
|
[self unlockFocus];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
- (char *) getTextureBuffer {
|
|
return data;
|
|
}
|
|
|
|
- (void) setFullScreen:(BOOL) flag {
|
|
if (!fullscreen && flag) {
|
|
// go to full screen
|
|
/* Create the new pixel format */
|
|
NSOpenGLPixelFormat *fmt;
|
|
NSOpenGLPixelFormatAttribute attribs[] = {
|
|
NSOpenGLPFAAccelerated,
|
|
NSOpenGLPFANoRecovery,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 24,
|
|
NSOpenGLPFAFullScreen,
|
|
NSOpenGLPFAScreenMask,
|
|
CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
|
|
0
|
|
};
|
|
|
|
fmt = [[NSOpenGLPixelFormat alloc]
|
|
initWithAttributes:attribs];
|
|
|
|
if (!fmt) {
|
|
GST_WARNING ("Cannot create NSOpenGLPixelFormat");
|
|
return;
|
|
}
|
|
|
|
/* Create the new OpenGL context */
|
|
fullScreenContext = [[NSOpenGLContext alloc]
|
|
initWithFormat: fmt shareContext:nil];
|
|
if (!fullScreenContext) {
|
|
GST_WARNING ("Failed to create new NSOpenGLContext");
|
|
return;
|
|
}
|
|
|
|
actualContext = fullScreenContext;
|
|
|
|
/* Capture display, switch to fullscreen */
|
|
if (CGCaptureAllDisplays () != CGDisplayNoErr) {
|
|
GST_WARNING ("CGCaptureAllDisplays() failed");
|
|
return;
|
|
}
|
|
[fullScreenContext setFullScreen];
|
|
[fullScreenContext makeCurrentContext];
|
|
|
|
fullscreen = YES;
|
|
|
|
[self initTextures];
|
|
[self setNeedsDisplay:YES];
|
|
|
|
} 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 ();
|
|
|
|
[self reshape];
|
|
[self initTextures];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
|
|
fullscreen = NO;
|
|
initDone = YES;
|
|
}
|
|
}
|
|
|
|
- (void) setVideoSize: (int)w : (int)h {
|
|
GST_LOG ("width:%d, height:%d", w, h);
|
|
|
|
width = w;
|
|
height = h;
|
|
|
|
[self initTextures];
|
|
[self reshape];
|
|
}
|
|
|
|
- (void) setKeepAspectRatio: (BOOL) flag {
|
|
keepAspectRatio = flag;
|
|
[self reshape];
|
|
}
|
|
|
|
#ifndef GSTREAMER_GLIB_COCOA_NSAPPLICATION
|
|
- (void) setMainThread: (NSThread *) thread {
|
|
mainThread = thread;
|
|
}
|
|
#endif
|
|
|
|
- (void) haveSuperviewReal:(NSMutableArray *)closure {
|
|
BOOL haveSuperview = [self superview] != nil;
|
|
[closure addObject:[NSNumber numberWithBool:haveSuperview]];
|
|
}
|
|
|
|
- (BOOL) haveSuperview {
|
|
NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
|
|
[self performSelector:@selector(haveSuperviewReal:)
|
|
onThread:mainThread
|
|
withObject:(id)closure waitUntilDone:YES];
|
|
|
|
return [[closure objectAtIndex:0] boolValue];
|
|
}
|
|
|
|
- (void) addToSuperviewReal:(NSView *)superview {
|
|
NSRect bounds;
|
|
[superview addSubview:self];
|
|
bounds = [superview bounds];
|
|
[self setFrame:bounds];
|
|
[self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
|
|
}
|
|
|
|
- (void) addToSuperview: (NSView *)superview {
|
|
[self performSelector:@selector(addToSuperviewReal:)
|
|
onThread:mainThread
|
|
withObject:superview waitUntilDone:YES];
|
|
}
|
|
|
|
- (void) removeFromSuperview: (id)unused
|
|
{
|
|
[self removeFromSuperview];
|
|
}
|
|
|
|
- (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];
|
|
}
|
|
|
|
- (void)updateTrackingAreas {
|
|
[self removeTrackingArea:trackingArea];
|
|
[trackingArea release];
|
|
trackingArea = [[NSTrackingArea alloc] initWithRect: [self bounds]
|
|
options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
|
|
owner:self userInfo:nil];
|
|
[self addTrackingArea:trackingArea];
|
|
}
|
|
|
|
- (BOOL)acceptsFirstResponder {
|
|
return YES;
|
|
}
|
|
|
|
- (void) setNavigation:(GstNavigation *)nav
|
|
{
|
|
navigation = nav;
|
|
}
|
|
|
|
- (void)sendMouseEvent:(NSEvent *)event : (const char *)event_name
|
|
{
|
|
NSPoint location;
|
|
gint button;
|
|
gdouble x, y;
|
|
|
|
if (!navigation)
|
|
return;
|
|
|
|
switch ([event type]) {
|
|
case NSMouseMoved:
|
|
button = 0;
|
|
break;
|
|
case NSLeftMouseDown:
|
|
case NSLeftMouseUp:
|
|
button = 1;
|
|
break;
|
|
case NSRightMouseDown:
|
|
case NSRightMouseUp:
|
|
button = 2;
|
|
break;
|
|
default:
|
|
button = 3;
|
|
break;
|
|
}
|
|
|
|
location = [self convertPoint:[event locationInWindow] fromView:nil];
|
|
|
|
x = location.x;
|
|
y = location.y;
|
|
/* invert Y */
|
|
|
|
y = (1 - ((gdouble) y) / [self bounds].size.height) * [self bounds].size.height;
|
|
|
|
gst_navigation_send_mouse_event (navigation, event_name, button, x, y);
|
|
}
|
|
|
|
- (void)sendKeyEvent:(NSEvent *)event : (const char *)event_name
|
|
{
|
|
if (!navigation)
|
|
return;
|
|
|
|
gst_navigation_send_key_event(navigation, event_name, gst_keycode_to_keyname([event keyCode]));
|
|
}
|
|
|
|
- (void)sendModifierKeyEvent:(NSEvent *)event
|
|
{
|
|
NSUInteger flags = [event modifierFlags];
|
|
const gchar* event_name = flags > savedModifierFlags ? "key-press" : "key-release";
|
|
savedModifierFlags = flags;
|
|
[self sendKeyEvent: event: event_name];
|
|
}
|
|
|
|
- (void)keyDown:(NSEvent *) event;
|
|
{
|
|
[self sendKeyEvent: event: "key-press"];
|
|
[super keyDown: event];
|
|
}
|
|
|
|
- (void)keyUp:(NSEvent *) event;
|
|
{
|
|
[self sendKeyEvent: event: "key-release"];
|
|
[super keyUp: event];
|
|
}
|
|
|
|
- (void)flagsChanged:(NSEvent *) event;
|
|
{
|
|
[self sendModifierKeyEvent: event];
|
|
[super flagsChanged: event];
|
|
}
|
|
|
|
- (void)mouseDown:(NSEvent *) event;
|
|
{
|
|
[self sendMouseEvent:event: "mouse-button-press"];
|
|
[super mouseDown: event];
|
|
}
|
|
|
|
- (void)mouseUp:(NSEvent *) event;
|
|
{
|
|
[self sendMouseEvent:event: "mouse-button-release"];
|
|
[super mouseUp: event];
|
|
}
|
|
|
|
- (void)mouseMoved:(NSEvent *)event;
|
|
{
|
|
[self sendMouseEvent:event: "mouse-move"];
|
|
[super mouseMoved: event];
|
|
}
|
|
|
|
- (void)mouseEntered:(NSEvent *)event;
|
|
{
|
|
[super mouseEntered: event];
|
|
}
|
|
|
|
- (void)mouseExited:(NSEvent *)event;
|
|
{
|
|
[super mouseExited: event];
|
|
}
|
|
|
|
@end
|