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