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