e004963c9a7493f292ad8efd621c87d80b8fcdcc
[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     NSOpenGLPFANoRecovery,
383     NSOpenGLPFADoubleBuffer,
384     NSOpenGLPFAColorSize, 24,
385     NSOpenGLPFAAlphaSize, 8,
386     NSOpenGLPFADepthSize, 24,
387     NSOpenGLPFAWindow,
388     0
389   };
390
391   fmt = [[NSOpenGLPixelFormat alloc]
392           initWithAttributes:attribs];
393
394   if (!fmt) {
395     GST_WARNING ("Cannot create NSOpenGLPixelFormat");
396     return nil;
397   }
398
399   self = [super initWithFrame: frame pixelFormat:fmt];
400   [fmt release];
401
402    actualContext = [self openGLContext];
403    [actualContext makeCurrentContext];
404    [actualContext update];
405
406   /* Black background */
407   glClearColor (0.0, 0.0, 0.0, 0.0);
408
409   pi_texture = 0;
410   data = nil;
411   width = frame.size.width;
412   height = frame.size.height;
413   drawingBounds = NSMakeRect(0, 0, width, height);
414
415   GST_LOG ("Width: %d Height: %d", width, height);
416
417   trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
418       options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
419       owner:self
420       userInfo:nil];
421
422   [self addTrackingArea:trackingArea];
423   mainThread = [NSThread mainThread];
424
425   [self initTextures];
426   return self;
427 }
428
429 - (NSRect) getDrawingBounds {
430   return drawingBounds;
431 }
432
433 - (void) reshape {
434   NSRect bounds;
435   gdouble frame_par, view_par;
436   gint view_height, view_width, c_height, c_width, c_x, c_y;
437
438
439   GST_LOG ("reshaping");
440
441   if (!initDone) {
442     return;
443   }
444
445   [actualContext makeCurrentContext];
446
447   bounds = [self bounds];
448   view_width = bounds.size.width;
449   view_height = bounds.size.height;
450
451   frame_par = (gdouble) width / height;
452   view_par = (gdouble) view_width / view_height;
453   if (!keepAspectRatio)
454     view_par = frame_par;
455
456   if (frame_par == view_par) {
457     c_height = view_height;
458     c_width = view_width;
459     c_x = 0;
460     c_y = 0;
461   } else if (frame_par < view_par) {
462     c_height = view_height;
463     c_width = c_height * frame_par;
464     c_x = (view_width - c_width) / 2;
465     c_y = 0;
466   } else {
467     c_width = view_width;
468     c_height = c_width / frame_par;
469     c_x = 0;
470     c_y = (view_height - c_height) / 2;
471   }
472
473   drawingBounds = NSMakeRect(c_x, c_y, c_width, c_height);
474   glViewport (c_x, c_y, (GLint) c_width, (GLint) c_height);
475 }
476
477 - (void) initTextures {
478
479   [actualContext makeCurrentContext];
480
481   /* Free previous texture if any */
482   if (pi_texture) {
483     glDeleteTextures (1, (GLuint *)&pi_texture);
484   }
485
486   if (data) {
487     data = g_realloc (data, width * height * sizeof(short)); // short or 3byte?
488   } else {
489     data = g_malloc0(width * height * sizeof(short));
490   }
491   /* Create textures */
492   glGenTextures (1, (GLuint *)&pi_texture);
493
494   glEnable (GL_TEXTURE_RECTANGLE_EXT);
495   glEnable (GL_UNPACK_CLIENT_STORAGE_APPLE);
496
497   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
498   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
499   
500   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
501
502   /* Use VRAM texturing */
503   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
504                    GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
505
506   /* Tell the driver not to make a copy of the texture but to use
507      our buffer */
508   glPixelStorei (GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
509
510   /* Linear interpolation */
511   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
512   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
513
514   /* I have no idea what this exactly does, but it seems to be
515      necessary for scaling */
516   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
517                    GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
518   glTexParameteri (GL_TEXTURE_RECTANGLE_EXT,
519                    GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
520   // glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); WHY ??
521
522   glTexImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
523                 width, height, 0, 
524                 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);
525
526
527   initDone = 1;
528 }
529
530 - (void) reloadTexture {
531   if (!initDone) {
532     return;
533   }
534
535   GST_LOG ("Reloading Texture");
536
537   [actualContext makeCurrentContext];
538
539   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture);
540   glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
541
542   /* glTexSubImage2D is faster than glTexImage2D
543      http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
544      TextureRange/MainOpenGLView.m.htm */
545   glTexSubImage2D (GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
546                    width, height,
547                    GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, data);    //FIXME
548 }
549
550 - (void) cleanUp {
551   initDone = 0;
552 }
553
554 - (void) drawQuad {
555   f_x = 1.0;
556   f_y = 1.0;
557
558   glBegin (GL_QUADS);
559   /* Top left */
560   glTexCoord2f (0.0, 0.0);
561   glVertex2f (-f_x, f_y);
562   /* Bottom left */
563   glTexCoord2f (0.0, (float) height);
564   glVertex2f (-f_x, -f_y);
565   /* Bottom right */
566   glTexCoord2f ((float) width, (float) height);
567   glVertex2f (f_x, -f_y);
568   /* Top right */
569   glTexCoord2f ((float) width, 0.0);
570   glVertex2f (f_x, f_y);
571   glEnd ();
572 }
573
574 - (void) drawRect:(NSRect) rect {
575   GLint params[] = { 1 };
576
577   [actualContext makeCurrentContext];
578
579   CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, params);
580
581   /* Black background */
582   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
583
584   if (!initDone) {
585     [actualContext flushBuffer];
586     return;
587   }
588
589   /* Draw */
590   glBindTexture (GL_TEXTURE_RECTANGLE_EXT, pi_texture); // FIXME
591   [self drawQuad];
592   /* Draw */
593   [actualContext flushBuffer];
594 }
595
596 - (void) displayTexture {
597   if ([self lockFocusIfCanDraw]) {
598
599     [self drawRect:[self bounds]];
600     [self reloadTexture];
601
602     [self unlockFocus];
603
604   }
605
606 }
607
608 - (char *) getTextureBuffer {
609   return data;
610 }
611
612 - (void) setFullScreen:(BOOL) flag {
613   if (!fullscreen && flag) {
614     // go to full screen
615     /* Create the new pixel format */
616     NSOpenGLPixelFormat *fmt;
617     NSOpenGLPixelFormatAttribute attribs[] = {
618       NSOpenGLPFAAccelerated,
619       NSOpenGLPFANoRecovery,
620       NSOpenGLPFADoubleBuffer,
621       NSOpenGLPFAColorSize, 24,
622       NSOpenGLPFAAlphaSize, 8,
623       NSOpenGLPFADepthSize, 24,
624       NSOpenGLPFAFullScreen,
625       NSOpenGLPFAScreenMask,
626       CGDisplayIDToOpenGLDisplayMask (kCGDirectMainDisplay),
627       0
628     };
629
630     fmt = [[NSOpenGLPixelFormat alloc]
631             initWithAttributes:attribs];
632
633     if (!fmt) {
634       GST_WARNING ("Cannot create NSOpenGLPixelFormat");
635       return;
636     }
637
638     /* Create the new OpenGL context */
639     fullScreenContext = [[NSOpenGLContext alloc]
640                           initWithFormat: fmt shareContext:nil];
641     if (!fullScreenContext) {
642       GST_WARNING ("Failed to create new NSOpenGLContext");
643       return;
644     }
645
646     actualContext = fullScreenContext;
647
648     /* Capture display, switch to fullscreen */
649     if (CGCaptureAllDisplays () != CGDisplayNoErr) {
650       GST_WARNING ("CGCaptureAllDisplays() failed");
651       return;
652     }
653     [fullScreenContext setFullScreen];
654     [fullScreenContext makeCurrentContext];
655
656     fullscreen = YES;
657
658     [self initTextures];
659     [self setNeedsDisplay:YES];
660
661   } else if (fullscreen && !flag) {
662     // fullscreen now and needs to go back to normal
663     initDone = NO;
664     
665     actualContext = [self openGLContext];
666
667     [NSOpenGLContext clearCurrentContext];
668     [fullScreenContext clearDrawable];
669     [fullScreenContext release];
670     fullScreenContext = nil;
671
672     CGReleaseAllDisplays ();
673
674     [self reshape];
675     [self initTextures];
676
677     [self setNeedsDisplay:YES];
678
679     fullscreen = NO;
680     initDone = YES;
681   }
682 }
683
684 - (void) setVideoSize: (int)w : (int)h {
685   GST_LOG ("width:%d, height:%d", w, h);
686
687   width = w;
688   height = h;
689
690 //  if (data) g_free(data);
691
692 //  data = g_malloc0 (2 * w * h);
693   [self initTextures];
694   [self reshape];
695 }
696
697 - (void) setKeepAspectRatio: (BOOL) flag {
698   keepAspectRatio = flag;
699   [self reshape];
700 }
701
702 - (void) setMainThread: (NSThread *) thread {
703   mainThread = thread;
704 }
705
706 - (void) haveSuperviewReal:(NSMutableArray *)closure {
707         BOOL haveSuperview = [self superview] != nil;
708         [closure addObject:[NSNumber numberWithBool:haveSuperview]];
709 }
710
711 - (BOOL) haveSuperview {
712         NSMutableArray *closure = [NSMutableArray arrayWithCapacity:1];
713         [self performSelector:@selector(haveSuperviewReal:)
714                 onThread:mainThread
715                 withObject:(id)closure waitUntilDone:YES];
716
717         return [[closure objectAtIndex:0] boolValue];
718 }
719
720 - (void) addToSuperviewReal:(NSView *)superview {
721         NSRect bounds;
722         [superview addSubview:self];
723         bounds = [superview bounds];
724         [self setFrame:bounds];
725         [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
726 }
727
728 - (void) addToSuperview: (NSView *)superview {
729         [self performSelector:@selector(addToSuperviewReal:)
730                 onThread:mainThread
731                 withObject:superview waitUntilDone:YES];
732 }
733
734 - (void) removeFromSuperview: (id)unused
735 {
736         [self removeFromSuperview];
737 }
738
739 - (void) dealloc {
740   GST_LOG ("dealloc called");
741   if (data) g_free(data);
742
743   if (fullScreenContext) {
744     [NSOpenGLContext clearCurrentContext];
745     [fullScreenContext clearDrawable];
746     [fullScreenContext release];
747     if (actualContext == fullScreenContext) actualContext = nil;
748     fullScreenContext = nil;
749   }
750
751   [super dealloc];
752 }
753
754 - (void)updateTrackingAreas {
755   [self removeTrackingArea:trackingArea];
756   [trackingArea release];
757   trackingArea = [[NSTrackingArea alloc] initWithRect: [self bounds]
758       options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow)
759       owner:self userInfo:nil];
760   [self addTrackingArea:trackingArea];
761 }
762
763 - (BOOL)acceptsFirstResponder {
764     return YES;
765 }
766
767 - (void) setNavigation:(GstNavigation *)nav
768 {
769   navigation = nav;
770 }
771
772 - (void)sendMouseEvent:(NSEvent *)event : (const char *)event_name
773 {
774   NSPoint location;
775   gint button;
776   gdouble x, y;
777
778   if (!navigation)
779     return;
780
781   switch ([event type]) {
782     case NSMouseMoved:
783       button = 0;
784       break;
785     case NSLeftMouseDown:
786     case NSLeftMouseUp:
787       button = 1;
788       break;
789     case NSRightMouseDown:
790     case NSRightMouseUp:
791       button = 2;
792       break;
793     default:
794       button = 3;
795       break;
796   }
797
798   location = [self convertPoint:[event locationInWindow] fromView:nil];
799
800   x = location.x;
801   y = location.y;
802   /* invert Y */
803
804   y = (1 - ((gdouble) y) / [self bounds].size.height) * [self bounds].size.height;
805
806   gst_navigation_send_mouse_event (navigation, event_name, button, x, y);
807 }
808
809 - (void)sendKeyEvent:(NSEvent *)event : (const char *)event_name
810 {
811   if (!navigation)
812     return;
813
814   gst_navigation_send_key_event(navigation, event_name, gst_keycode_to_keyname([event keyCode]));
815 }
816
817 - (void)sendModifierKeyEvent:(NSEvent *)event
818 {
819   NSUInteger flags = [event modifierFlags];
820   const gchar* event_name = flags > savedModifierFlags ? "key-press" : "key-release";
821   savedModifierFlags = flags;
822   [self sendKeyEvent: event: event_name];
823 }
824
825 - (void)keyDown:(NSEvent *) event;
826 {
827   [self sendKeyEvent: event: "key-press"];
828   [super keyDown: event];
829 }
830
831 - (void)keyUp:(NSEvent *) event;
832 {
833   [self sendKeyEvent: event: "key-release"];
834   [super keyUp: event];
835 }
836
837 - (void)flagsChanged:(NSEvent *) event;
838 {
839   [self sendModifierKeyEvent: event];
840   [super flagsChanged: event];
841 }
842
843 - (void)mouseDown:(NSEvent *) event;
844 {
845   [self sendMouseEvent:event: "mouse-button-press"];
846   [super mouseDown: event];
847 }
848
849 - (void)mouseUp:(NSEvent *) event;
850 {
851   [self sendMouseEvent:event: "mouse-button-release"];
852   [super mouseUp: event];
853 }
854
855 - (void)mouseMoved:(NSEvent *)event;
856 {
857   [self sendMouseEvent:event: "mouse-move"];
858   [super mouseMoved: event];
859 }
860
861 - (void)mouseEntered:(NSEvent *)event;
862 {
863   [super mouseEntered: event];
864 }
865
866 - (void)mouseExited:(NSEvent *)event;
867 {
868   [super mouseExited: event];
869 }
870
871 @end