osxvideosink: fix segfault accessing osxwindow when not set yet
[platform/upstream/gst-plugins-good.git] / sys / osxvideo / cocoawindow.m
1 /* GStreamer
2  * Copyright (C) 2004 Zaheer Abbas Merali <zaheerabbas at merali dot org>
3  * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
4  *
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.
9  *
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.
14  *
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.
19  * 
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
22  *
23  */
24
25 /* inspiration gained from looking at source of osx video out of xine and vlc
26  * and is reflected in the code
27  */
28
29
30 #include <Cocoa/Cocoa.h>
31 #include <gst/gst.h>
32 #import "cocoawindow.h"
33 #import "osxvideosink.h"
34
35 #include <OpenGL/OpenGL.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
38
39 #include <Carbon/Carbon.h>
40
41 /* Debugging category */
42 #include <gst/gstinfo.h>
43
44 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
45 #define kVK_ANSI_Keypad0 0x52
46 #define kVK_ANSI_Keypad1 0x53
47 #define kVK_ANSI_Keypad2 0x54
48 #define kVK_ANSI_Keypad3 0x55
49 #define kVK_ANSI_Keypad4 0x56
50 #define kVK_ANSI_Keypad5 0x57
51 #define kVK_ANSI_Keypad6 0x58
52 #define kVK_ANSI_Keypad7 0x59
53 #define kVK_ANSI_Keypad8 0x5b
54 #define kVK_ANSI_Keypad9 0x5c
55 #define kVK_ANSI_KeypadDecimal 0x41
56 #define kVK_ANSI_KeypadDivide 0x4b
57 #define kVK_ANSI_KeypadEnter 0x4c
58 #define kVK_ANSI_KeypadMinus 0x4e
59 #define kVK_ANSI_KeypadMultiply 0x43
60 #define kVK_ANSI_KeypadPlus 0x45
61 #define kVK_Control 0x3b
62 #define kVK_Delete 0x33
63 #define kVK_DownArrow 0x7d
64 #define kVK_End 0x77
65 #define kVK_Escape 0x35
66 #define kVK_F1 0x7a
67 #define kVK_F10 0x6d
68 #define kVK_F11 0x67
69 #define kVK_F12 0x6f
70 #define kVK_F2 0x78
71 #define kVK_F3 0x63
72 #define kVK_F4 0x76
73 #define kVK_F5 0x60
74 #define kVK_F6 0x61
75 #define kVK_F7 0x62
76 #define kVK_F8 0x64
77 #define kVK_F9 0x65
78 #define kVK_ForwardDelete 0x75
79 #define kVK_Help 0x72
80 #define kVK_Home 0x73
81 #define kVK_LeftArrow 0x7b
82 #define kVK_Option 0x3a
83 #define kVK_PageDown 0x79
84 #define kVK_PageUp 0x74
85 #define kVK_Return 0x24
86 #define kVK_RightArrow 0x7c
87 #define kVK_Shift 0x38
88 #define kVK_Tab 0x30
89 #define kVK_UpArrow 0x7e
90 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED <= 1040 */
91
92 static
93 const gchar* gst_keycode_to_keyname(gint16 keycode)
94 {
95     switch (keycode)
96     {
97       case kVK_ANSI_A:
98         return "a";
99       case kVK_ANSI_S:
100         return "s";
101       case kVK_ANSI_D:
102         return "d";
103       case kVK_ANSI_F:
104         return "f";
105       case kVK_ANSI_H:
106         return "h";
107       case kVK_ANSI_G:
108         return "g";
109       case kVK_ANSI_Z:
110         return "z";
111       case kVK_ANSI_X:
112         return "x";
113       case kVK_ANSI_C:
114         return "c";
115       case kVK_ANSI_V:
116         return "v";
117       case kVK_ANSI_B:
118         return "b";
119       case kVK_ANSI_Q:
120         return "q";
121       case kVK_ANSI_W:
122         return "w";
123       case kVK_ANSI_E:
124         return "e";
125       case kVK_ANSI_R:
126         return "r";
127       case kVK_ANSI_Y:
128         return "y";
129       case kVK_ANSI_T:
130         return "t";
131       case kVK_ANSI_1:
132         return "1";
133       case kVK_ANSI_2:
134         return "2";
135       case kVK_ANSI_3:
136         return "3";
137       case kVK_ANSI_4:
138         return "4";
139       case kVK_ANSI_6:
140         return "6";
141       case kVK_ANSI_5:
142         return "5";
143       case kVK_ANSI_Equal:
144         return "equal";
145       case kVK_ANSI_9:
146         return "9";
147       case kVK_ANSI_7:
148         return "7";
149       case kVK_ANSI_Minus:
150         return "minus";
151       case kVK_ANSI_8:
152         return "8";
153       case kVK_ANSI_0:
154         return "0";
155       case kVK_ANSI_RightBracket:
156         return "bracketright";
157       case kVK_ANSI_O:
158         return "0";
159       case kVK_ANSI_U:
160         return "u";
161       case kVK_ANSI_LeftBracket:
162         return "bracketleft";
163       case kVK_ANSI_I:
164         return "i";
165       case kVK_ANSI_P:
166         return "p";
167       case kVK_ANSI_L:
168         return "l";
169       case kVK_ANSI_J:
170         return "j";
171       case kVK_ANSI_Quote:
172         return "apostrophe";
173       case kVK_ANSI_K:
174         return "k";
175       case kVK_ANSI_Semicolon:
176         return "semicolon";
177       case kVK_ANSI_Backslash:
178         return "backslash";
179       case kVK_ANSI_Comma:
180         return "comma";
181       case kVK_ANSI_Slash:
182         return "slash";
183       case kVK_ANSI_N:
184         return "n";
185       case kVK_ANSI_M:
186         return "m";
187       case kVK_ANSI_Period:
188         return "period";
189       case kVK_ANSI_Grave:
190         return "grave";
191       case kVK_ANSI_KeypadDecimal:
192         return "KP_Delete";
193       case kVK_ANSI_KeypadMultiply:
194         return "KP_Multiply";
195       case kVK_ANSI_KeypadPlus:
196         return "KP_Add";
197       case kVK_ANSI_KeypadClear:
198         return "KP_Clear";
199       case kVK_ANSI_KeypadDivide:
200         return "KP_Divide";
201       case kVK_ANSI_KeypadEnter:
202         return "KP_Enter";
203       case kVK_ANSI_KeypadMinus:
204         return "KP_Subtract";
205       case kVK_ANSI_KeypadEquals:
206         return "KP_Equals";
207       case kVK_ANSI_Keypad0:
208         return "KP_Insert";
209       case kVK_ANSI_Keypad1:
210         return "KP_End";
211       case kVK_ANSI_Keypad2:
212         return "KP_Down";
213       case kVK_ANSI_Keypad3:
214         return "KP_Next";
215       case kVK_ANSI_Keypad4:
216         return "KP_Left";
217       case kVK_ANSI_Keypad5:
218         return "KP_Begin";
219       case kVK_ANSI_Keypad6:
220         return "KP_Right";
221       case kVK_ANSI_Keypad7:
222         return "KP_Home";
223       case kVK_ANSI_Keypad8:
224         return "KP_Up";
225       case kVK_ANSI_Keypad9:
226         return "KP_Prior";
227
228     /* keycodes for keys that are independent of keyboard layout*/
229
230       case kVK_Return:
231         return "Return";
232       case kVK_Tab:
233         return "Tab";
234       case kVK_Space:
235         return "space";
236       case kVK_Delete:
237         return "Backspace";
238       case kVK_Escape:
239         return "Escape";
240       case kVK_Command:
241         return "Command";
242       case kVK_Shift:
243         return "Shift_L";
244       case kVK_CapsLock:
245         return "Caps_Lock";
246       case kVK_Option:
247         return "Option_L";
248       case kVK_Control:
249         return "Control_L";
250       case kVK_RightShift:
251         return "Shift_R";
252       case kVK_RightOption:
253         return "Option_R";
254       case kVK_RightControl:
255         return "Control_R";
256       case kVK_Function:
257         return "Function";
258       case kVK_F17:
259         return "F17";
260       case kVK_VolumeUp:
261         return "VolumeUp";
262       case kVK_VolumeDown:
263         return "VolumeDown";
264       case kVK_Mute:
265         return "Mute";
266       case kVK_F18:
267         return "F18";
268       case kVK_F19:
269         return "F19";
270       case kVK_F20:
271         return "F20";
272       case kVK_F5:
273         return "F5";
274       case kVK_F6:
275         return "F6";
276       case kVK_F7:
277         return "F7";
278       case kVK_F3:
279         return "F3";
280       case kVK_F8:
281         return "F8";
282       case kVK_F9:
283         return "F9";
284       case kVK_F11:
285         return "F11";
286       case kVK_F13:
287         return "F13";
288       case kVK_F16:
289         return "F16";
290       case kVK_F14:
291         return "F14";
292       case kVK_F10:
293         return "F10";
294       case kVK_F12:
295         return "F12";
296       case kVK_F15:
297         return "F15";
298       case kVK_Help:
299         return "Help";
300       case kVK_Home:
301         return "Home";
302       case kVK_PageUp:
303         return "Prior";
304       case kVK_ForwardDelete:
305         return "Delete";
306       case kVK_F4:
307         return "F4";
308       case kVK_End:
309         return "End";
310       case kVK_F2:
311         return "F2";
312       case kVK_PageDown:
313         return "Next";
314       case kVK_F1:
315         return "F1";
316       case kVK_LeftArrow:
317         return "Left";
318       case kVK_RightArrow:
319         return "Right";
320       case kVK_DownArrow:
321         return "Down";
322       case kVK_UpArrow:
323         return "Up";
324     default:
325         return "";
326   };
327 }
328
329 @ implementation GstOSXVideoSinkWindow
330
331 /* The object has to be released */
332 - (id) initWithContentNSRect: (NSRect) rect
333                  styleMask: (unsigned int) styleMask
334                    backing: (NSBackingStoreType) bufferingType 
335                      defer: (BOOL) flag
336                     screen:(NSScreen *) aScreen
337 {
338   self = [super initWithContentRect: rect
339                 styleMask: styleMask
340                 backing: bufferingType 
341                 defer: flag 
342                 screen:aScreen];
343
344   GST_DEBUG ("Initializing GstOSXvideoSinkWindow");
345
346   gstview = [[GstGLView alloc] initWithFrame:rect];
347   
348   if (gstview)
349     [self setContentView:gstview];
350   [self setTitle:@"GStreamer Video Output"];
351
352   return self;
353 }
354
355 - (void) setContentSize:(NSSize) size {
356   width = size.width;
357   height = size.height;
358
359   [super setContentSize:size];
360 }
361
362 - (GstGLView *) gstView {
363   return gstview;
364 }
365
366 - (void) awakeFromNib {
367   [self setAcceptsMouseMovedEvents:YES];
368 }
369
370 @end
371
372
373 //
374 // OpenGL implementation
375 //
376
377 @ implementation GstGLView
378
379 - (id) initWithFrame:(NSRect) frame {
380   NSOpenGLPixelFormat *fmt;
381   NSOpenGLPixelFormatAttribute attribs[] = {
382     NSOpenGLPFAAccelerated,
383     NSOpenGLPFANoRecovery,
384     NSOpenGLPFADoubleBuffer,
385     NSOpenGLPFAColorSize, 24,
386     NSOpenGLPFAAlphaSize, 8,
387     NSOpenGLPFADepthSize, 24,
388     NSOpenGLPFAWindow,
389     0
390   };
391
392   fmt = [[NSOpenGLPixelFormat alloc]
393           initWithAttributes:attribs];
394
395   if (!fmt) {
396     GST_WARNING ("Cannot create NSOpenGLPixelFormat");
397     return nil;
398   }
399
400   self = [super initWithFrame: frame pixelFormat:fmt];
401   [fmt release];
402
403    actualContext = [self openGLContext];
404    [actualContext makeCurrentContext];
405    [actualContext update];
406
407   /* Black background */
408   glClearColor (0.0, 0.0, 0.0, 0.0);
409
410   pi_texture = 0;
411   data = nil;
412   width = frame.size.width;
413   height = frame.size.height;
414   drawingBounds = NSMakeRect(0, 0, width, height);
415
416   GST_LOG ("Width: %d Height: %d", width, height);
417
418   trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
419       options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
420       owner:self
421       userInfo:nil];
422
423   [self addTrackingArea:trackingArea];
424   mainThread = [NSThread mainThread];
425
426   [self initTextures];
427   return self;
428 }
429
430 - (NSRect) getDrawingBounds {
431   return drawingBounds;
432 }
433
434 - (void) reshape {
435   NSRect bounds;
436   gdouble frame_par, view_par;
437   gint view_height, view_width, c_height, c_width, c_x, c_y;
438
439
440   GST_LOG ("reshaping");
441
442   if (!initDone) {
443     return;
444   }
445
446   [actualContext makeCurrentContext];
447
448   bounds = [self bounds];
449   view_width = bounds.size.width;
450   view_height = bounds.size.height;
451
452   frame_par = (gdouble) width / height;
453   view_par = (gdouble) view_width / view_height;
454   if (!keepAspectRatio)
455     view_par = frame_par;
456
457   if (frame_par == view_par) {
458     c_height = view_height;
459     c_width = view_width;
460     c_x = 0;
461     c_y = 0;
462   } else if (frame_par < view_par) {
463     c_height = view_height;
464     c_width = c_height * frame_par;
465     c_x = (view_width - c_width) / 2;
466     c_y = 0;
467   } else {
468     c_width = view_width;
469     c_height = c_width / frame_par;
470     c_x = 0;
471     c_y = (view_height - c_height) / 2;
472   }
473
474   drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
475   glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
476 }
477
478 - (void) initTextures {
479
480   [actualContext makeCurrentContext];
481
482   /* Free previous texture if any */
483   if (pi_texture) {
484     glDeleteTextures (1, (GLuint *)&pi_texture);
485   }
486
487   if (data) {
488     data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
489   } else {
490     data = g_malloc0(width * height * sizeof(short));
491   }
492   /* Create textures */
493   glGenTextures (1, (GLuint *)&pi_texture);
494
495   glEnable (GL_TEXTURE_RECTANGLE_EXT);
496   glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
497
498   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
499   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
500   
501   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
502
503   /* Use VRAM texturing */
504   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
505                    GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
506
507   /* Tell the driver not to make a copy of the texture but to use
508      our buffer */
509   glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
510
511   /* Linear interpolation */
512   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
513   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
514
515   /* I have no idea what this exactly does, but it seems to be
516      necessary for scaling */
517   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
518                    GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
519   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
520                    GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
521   // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
522
523   glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
524                 width, height, 0, 
525                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
526
527
528   initDone = 1;
529 }
530
531 - (void) reloadTexture {
532   if (!initDone) {
533     return;
534   }
535
536   GST_LOG ("Reloading Texture");
537
538   [actualContext makeCurrentContext];
539
540   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
541   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
542
543   /* glTexSubImage2D is faster than glTexImage2D
544      http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
545      TextureRange/MainOpenGLView.m.htm */
546   glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
547                    width, height,
548                    GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);    //FIXME
549 }
550
551 - (void) cleanUp {
552   initDone = 0;
553 }
554
555 - (void) drawQuad {
556   f_x = 1.0;
557   f_y = 1.0;
558
559   glBegin (GL_QUADS);
560   /* Top left */
561   glTexCoord2f (0.0, 0.0);
562   glVertex2f (-f_x, f_y);
563   /* Bottom left */
564   glTexCoord2f (0.0, (float) height);
565   glVertex2f (-f_x, -f_y);
566   /* Bottom right */
567   glTexCoord2f ((float) width, (float) height);
568   glVertex2f (f_x, -f_y);
569   /* Top right */
570   glTexCoord2f ((float) width, 0.0);
571   glVertex2f (f_x, f_y);
572   glEnd ();
573 }
574
575 - (void) drawRect:(NSRect) rect {
576   GLint params[] = { 1 };
577
578   [actualContext makeCurrentContext];
579
580   CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
581
582   /* Black background */
583   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
584
585   if (!initDone) {
586     [actualContext flushBuffer];
587     return;
588   }
589
590   /* Draw */
591   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
592   [self drawQuad];
593   /* Draw */
594   [actualContext flushBuffer];
595 }
596
597 - (void) displayTexture {
598   if ([self lockFocusIfCanDraw]) {
599
600     [self drawRect:[self bounds]];
601     [self reloadTexture];
602
603     [self unlockFocus];
604
605   }
606
607 }
608
609 - (char *) getTextureBuffer {
610   return data;
611 }
612
613 - (void) setFullScreen:(BOOL) flag {
614   if (!fullscreen && flag) {
615     // go to full screen
616     /* Create the new pixel format */
617     NSOpenGLPixelFormat *fmt;
618     NSOpenGLPixelFormatAttribute attribs[] = {
619       NSOpenGLPFAAccelerated,
620       NSOpenGLPFANoRecovery,
621       NSOpenGLPFADoubleBuffer,
622       NSOpenGLPFAColorSize, 24,
623       NSOpenGLPFAAlphaSize, 8,
624       NSOpenGLPFADepthSize, 24,
625       NSOpenGLPFAFullScreen,
626       NSOpenGLPFAScreenMask,
627       CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
628       0
629     };
630
631     fmt = [[NSOpenGLPixelFormat alloc]
632             initWithAttributes:attribs];
633
634     if (!fmt) {
635       GST_WARNING ("Cannot create NSOpenGLPixelFormat");
636       return;
637     }
638
639     /* Create the new OpenGL context */
640     fullScreenContext = [[NSOpenGLContext alloc]
641                           initWithFormat: fmt shareContext:nil];
642     if (!fullScreenContext) {
643       GST_WARNING ("Failed to create new NSOpenGLContext");
644       return;
645     }
646
647     actualContext = fullScreenContext;
648
649     /* Capture display, switch to fullscreen */
650     if (CGCaptureAllDisplays () != CGDisplayNoErr) {
651       GST_WARNING ("CGCaptureAllDisplays() failed");
652       return;
653     }
654     [fullScreenContext setFullScreen];
655     [fullScreenContext makeCurrentContext];
656
657     fullscreen = YES;
658
659     [self initTextures];
660     [self setNeedsDisplay:YES];
661
662   } else if (fullscreen && !flag) {
663     // fullscreen now and needs to go back to normal
664     initDone = NO;
665     
666     actualContext = [self openGLContext];
667
668     [NSOpenGLContext clearCurrentContext];
669     [fullScreenContext clearDrawable];
670     [fullScreenContext release];
671     fullScreenContext = nil;
672
673     CGReleaseAllDisplays ();
674
675     [self reshape];
676     [self initTextures];
677
678     [self setNeedsDisplay:YES];
679
680     fullscreen = NO;
681     initDone = YES;
682   }
683 }
684
685 - (void) setVideoSize: (int) w:(int) h {
686   GST_LOG ("width:%d, height:%d", w, h);
687
688   width = w;
689   height = h;
690
691 //  if (data) g_free(data);
692
693 //  data = g_malloc0 (2 * w * h);
694   [self initTextures];
695   [self reshape];
696 }
697
698 - (void) setKeepAspectRatio: (BOOL) flag {
699   keepAspectRatio = flag;
700   [self reshape];
701 }
702
703 - (void) setMainThread: (NSThread *) thread {
704   mainThread = thread;
705 }
706
707 - (void) haveSuperviewReal:(NSMutableArray *)closure {
708         BOOL haveSuperview = [self superview] != nil;
709         [closure addObject:[NSNumber numberWithBool:haveSuperview]];
710 }
711
712 - (BOOL) haveSuperview {
713         NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
714         [self performSelector:@selector(haveSuperviewReal:)
715                 onThread:mainThread
716                 withObject:(id)closure waitUntilDone:YES];
717
718         return [[closure objectAtIndex:0] boolValue];
719 }
720
721 - (void) addToSuperviewReal:(NSView *)superview {
722         NSRect bounds;
723         [superview addSubview:self];
724         bounds = [superview bounds];
725         [self setFrame:bounds];
726         [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
727 }
728
729 - (void) addToSuperview: (NSView *)superview {
730         [self performSelector:@selector(addToSuperviewReal:)
731                 onThread:mainThread
732                 withObject:superview waitUntilDone:YES];
733 }
734
735 - (void) removeFromSuperview: (id)unused
736 {
737         [self removeFromSuperview];
738 }
739
740 - (void) dealloc {
741   GST_LOG ("dealloc called");
742   if (data) g_free(data);
743
744   if (fullScreenContext) {
745     [NSOpenGLContext clearCurrentContext];
746     [fullScreenContext clearDrawable];
747     [fullScreenContext release];
748     if (actualContext == fullScreenContext) actualContext = nil;
749     fullScreenContext = nil;
750   }
751
752   [super dealloc];
753 }
754
755 - (void)updateTrackingAreas {
756   [self removeTrackingArea:trackingArea];
757   [trackingArea release];
758   trackingArea = [[NSTrackingArea alloc] initWithRect: [self bounds]
759       options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
760       owner:self userInfo:nil];
761   [self addTrackingArea:trackingArea];
762 }
763
764 - (BOOL)acceptsFirstResponder {
765     return YES;
766 }
767
768 - (void) setNavigation:(GstNavigation *)nav
769 {
770   navigation = nav;
771 }
772
773 - (void)sendMouseEvent:(NSEvent *)event: (const char *)event_name
774 {
775   NSPoint location;
776   gint button;
777   gdouble x, y;
778
779   if (!navigation)
780     return;
781
782   switch ([event type]) {
783     case NSMouseMoved:
784       button = 0;
785       break;
786     case NSLeftMouseDown:
787     case NSLeftMouseUp:
788       button = 1;
789       break;
790     case NSRightMouseDown:
791     case NSRightMouseUp:
792       button = 2;
793       break;
794     default:
795       button = 3;
796       break;
797   }
798
799   location = [self convertPoint:[event locationInWindow] fromView:nil];
800
801   x = location.x;
802   y = location.y;
803   /* invert Y */
804
805   y = (1 - ((gdouble) y) / [self bounds].size.height) * [self bounds].size.height;
806
807   gst_navigation_send_mouse_event (navigation, event_name, button, x, y);
808 }
809
810 - (void)sendKeyEvent:(NSEvent *)event: (const char *)event_name
811 {
812   if (!navigation)
813     return;
814
815   gst_navigation_send_key_event(navigation, event_name, gst_keycode_to_keyname([event keyCode]));
816 }
817
818 - (void)sendModifierKeyEvent:(NSEvent *)event
819 {
820   NSUInteger flags = [event modifierFlags];
821   const gchar* event_name = flags > savedModifierFlags ? "key-press" : "key-release";
822   savedModifierFlags = flags;
823   [self sendKeyEvent: event: event_name];
824 }
825
826 - (void)keyDown:(NSEvent *) event;
827 {
828   [self sendKeyEvent: event: "key-press"];
829   [super keyDown: event];
830 }
831
832 - (void)keyUp:(NSEvent *) event;
833 {
834   [self sendKeyEvent: event: "key-release"];
835   [super keyUp: event];
836 }
837
838 - (void)flagsChanged:(NSEvent *) event;
839 {
840   [self sendModifierKeyEvent: event];
841   [super flagsChanged: event];
842 }
843
844 - (void)mouseDown:(NSEvent *) event;
845 {
846   [self sendMouseEvent:event: "mouse-button-press"];
847   [super mouseDown: event];
848 }
849
850 - (void)mouseUp:(NSEvent *) event;
851 {
852   [self sendMouseEvent:event: "mouse-button-release"];
853   [super mouseUp: event];
854 }
855
856 - (void)mouseMoved:(NSEvent *)event;
857 {
858   [self sendMouseEvent:event: "mouse-move"];
859   [super mouseMoved: event];
860 }
861
862 - (void)mouseEntered:(NSEvent *)event;
863 {
864   [super mouseEntered: event];
865 }
866
867 - (void)mouseExited:(NSEvent *)event;
868 {
869   [super mouseExited: event];
870 }
871
872 @end