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