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