X11Application.m revision 35c4bbdf
1/* X11Application.m -- subclass of NSApplication to multiplex events 2 * 3 * Copyright (c) 2002-2012 Apple Inc. All rights reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person 6 * obtaining a copy of this software and associated documentation files 7 * (the "Software"), to deal in the Software without restriction, 8 * including without limitation the rights to use, copy, modify, merge, 9 * publish, distribute, sublicense, and/or sell copies of the Software, 10 * and to permit persons to whom the Software is furnished to do so, 11 * subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 20 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Except as contained in this notice, the name(s) of the above 26 * copyright holders shall not be used in advertising or otherwise to 27 * promote the sale, use or other dealings in this Software without 28 * prior written authorization. 29 */ 30 31#include "sanitizedCarbon.h" 32 33#ifdef HAVE_DIX_CONFIG_H 34#include <dix-config.h> 35#endif 36 37#include "quartzCommon.h" 38 39#import "X11Application.h" 40 41#include "darwin.h" 42#include "quartz.h" 43#include "darwinEvents.h" 44#include "quartzKeyboard.h" 45#include <X11/extensions/applewmconst.h> 46#include "micmap.h" 47#include "exglobals.h" 48 49#include <mach/mach.h> 50#include <unistd.h> 51#include <AvailabilityMacros.h> 52 53#include <pthread.h> 54 55#include <Xplugin.h> 56 57// pbproxy/pbproxy.h 58extern int 59xpbproxy_run(void); 60 61#define DEFAULTS_FILE X11LIBDIR "/X11/xserver/Xquartz.plist" 62 63#ifndef XSERVER_VERSION 64#define XSERVER_VERSION "?" 65#endif 66 67#ifdef HAVE_LIBDISPATCH 68#include <dispatch/dispatch.h> 69 70static dispatch_queue_t eventTranslationQueue; 71#endif 72 73#ifndef __has_feature 74#define __has_feature(x) 0 75#endif 76 77#ifndef CF_RETURNS_RETAINED 78#if __has_feature(attribute_cf_returns_retained) 79#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) 80#else 81#define CF_RETURNS_RETAINED 82#endif 83#endif 84 85extern Bool noTestExtensions; 86extern Bool noRenderExtension; 87extern BOOL serverRunning; 88 89#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 90static TISInputSourceRef last_key_layout; 91#else 92static KeyboardLayoutRef last_key_layout; 93#endif 94 95/* This preference is only tested on Lion or later as it's not relevant to 96 * earlier OS versions. 97 */ 98Bool XQuartzScrollInDeviceDirection = FALSE; 99 100extern int darwinFakeButtons; 101 102/* Store the mouse location while in the background, and update X11's pointer 103 * location when we become the foreground application 104 */ 105static NSPoint bgMouseLocation; 106static BOOL bgMouseLocationUpdated = FALSE; 107 108X11Application *X11App; 109 110CFStringRef app_prefs_domain_cfstr = NULL; 111 112#define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | \ 113 NSAlternateKeyMask | NSCommandKeyMask) 114 115@interface X11Application (Private) 116- (void) sendX11NSEvent:(NSEvent *)e; 117@end 118 119@implementation X11Application 120 121typedef struct message_struct message; 122struct message_struct { 123 mach_msg_header_t hdr; 124 SEL selector; 125 NSObject *arg; 126}; 127 128static mach_port_t _port; 129 130/* Quartz mode initialization routine. This is often dynamically loaded 131 but is statically linked into this X server. */ 132Bool 133QuartzModeBundleInit(void); 134 135static void 136init_ports(void) 137{ 138 kern_return_t r; 139 NSPort *p; 140 141 if (_port != MACH_PORT_NULL) return; 142 143 r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_port); 144 if (r != KERN_SUCCESS) return; 145 146 p = [NSMachPort portWithMachPort:_port]; 147 [p setDelegate:NSApp]; 148 [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode: 149 NSDefaultRunLoopMode]; 150} 151 152static void 153message_kit_thread(SEL selector, NSObject *arg) 154{ 155 message msg; 156 kern_return_t r; 157 158 msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 159 msg.hdr.msgh_size = sizeof(msg); 160 msg.hdr.msgh_remote_port = _port; 161 msg.hdr.msgh_local_port = MACH_PORT_NULL; 162 msg.hdr.msgh_reserved = 0; 163 msg.hdr.msgh_id = 0; 164 165 msg.selector = selector; 166 msg.arg = [arg retain]; 167 168 r = mach_msg(&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size, 169 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 170 if (r != KERN_SUCCESS) 171 ErrorF("%s: mach_msg failed: %x\n", __FUNCTION__, r); 172} 173 174- (void) handleMachMessage:(void *)_msg 175{ 176 message *msg = _msg; 177 178 [self performSelector:msg->selector withObject:msg->arg]; 179 [msg->arg release]; 180} 181 182- (void) set_controller:obj 183{ 184 if (_controller == nil) _controller = [obj retain]; 185} 186 187- (void) dealloc 188{ 189 if (_controller != nil) [_controller release]; 190 191 if (_port != MACH_PORT_NULL) 192 mach_port_deallocate(mach_task_self(), _port); 193 194 [super dealloc]; 195} 196 197- (void) orderFrontStandardAboutPanel: (id) sender 198{ 199 NSMutableDictionary *dict; 200 NSDictionary *infoDict; 201 NSString *tem; 202 203 dict = [NSMutableDictionary dictionaryWithCapacity:3]; 204 infoDict = [[NSBundle mainBundle] infoDictionary]; 205 206 [dict setObject: NSLocalizedString(@"The X Window System", @"About panel") 207 forKey:@"ApplicationName"]; 208 209 tem = [infoDict objectForKey:@"CFBundleShortVersionString"]; 210 211 [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem] 212 forKey:@"ApplicationVersion"]; 213 214 [dict setObject:[NSString stringWithFormat:@"xorg-server %s", 215 XSERVER_VERSION] 216 forKey:@"Version"]; 217 218 [self orderFrontStandardAboutPanelWithOptions: dict]; 219} 220 221- (void) activateX:(OSX_BOOL)state 222{ 223 if (_x_active == state) 224 return; 225 226 DEBUG_LOG("state=%d, _x_active=%d, \n", state, _x_active); 227 if (state) { 228 if (bgMouseLocationUpdated) { 229 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0, 230 bgMouseLocation.x, bgMouseLocation.y, 231 0.0, 0.0); 232 bgMouseLocationUpdated = FALSE; 233 } 234 DarwinSendDDXEvent(kXquartzActivate, 0); 235 } 236 else { 237 238 if (darwin_all_modifier_flags) 239 DarwinUpdateModKeys(0); 240 241 DarwinInputReleaseButtonsAndKeys(darwinKeyboard); 242 DarwinInputReleaseButtonsAndKeys(darwinPointer); 243 DarwinInputReleaseButtonsAndKeys(darwinTabletCursor); 244 DarwinInputReleaseButtonsAndKeys(darwinTabletStylus); 245 DarwinInputReleaseButtonsAndKeys(darwinTabletEraser); 246 247 DarwinSendDDXEvent(kXquartzDeactivate, 0); 248 } 249 250 _x_active = state; 251} 252 253- (void) became_key:(NSWindow *)win 254{ 255 [self activateX:NO]; 256} 257 258- (void) sendEvent:(NSEvent *)e 259{ 260 OSX_BOOL for_appkit, for_x; 261 262 /* By default pass down the responder chain and to X. */ 263 for_appkit = YES; 264 for_x = YES; 265 266 switch ([e type]) { 267 case NSLeftMouseDown: 268 case NSRightMouseDown: 269 case NSOtherMouseDown: 270 case NSLeftMouseUp: 271 case NSRightMouseUp: 272 case NSOtherMouseUp: 273 if ([e window] != nil) { 274 /* Pointer event has an (AppKit) window. Probably something for the kit. */ 275 for_x = NO; 276 if (_x_active) [self activateX:NO]; 277 } 278 else if ([self modalWindow] == nil) { 279 /* Must be an X window. Tell appkit it doesn't have focus. */ 280 for_appkit = NO; 281 282 if ([self isActive]) { 283 [self deactivate]; 284 if (!_x_active && quartzProcs->IsX11Window([e windowNumber])) 285 [self activateX:YES]; 286 } 287 } 288 289 /* We want to force sending to appkit if we're over the menu bar */ 290 if (!for_appkit) { 291 NSPoint NSlocation = [e locationInWindow]; 292 NSWindow *window = [e window]; 293 NSRect NSframe, NSvisibleFrame; 294 CGRect CGframe, CGvisibleFrame; 295 CGPoint CGlocation; 296 297 if (window != nil) { 298 NSRect frame = [window frame]; 299 NSlocation.x += frame.origin.x; 300 NSlocation.y += frame.origin.y; 301 } 302 303 NSframe = [[NSScreen mainScreen] frame]; 304 NSvisibleFrame = [[NSScreen mainScreen] visibleFrame]; 305 306 CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y, 307 NSframe.size.width, NSframe.size.height); 308 CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x, 309 NSvisibleFrame.origin.y, 310 NSvisibleFrame.size.width, 311 NSvisibleFrame.size.height); 312 CGlocation = CGPointMake(NSlocation.x, NSlocation.y); 313 314 if (CGRectContainsPoint(CGframe, CGlocation) && 315 !CGRectContainsPoint(CGvisibleFrame, CGlocation)) 316 for_appkit = YES; 317 } 318 319 break; 320 321 case NSKeyDown: 322 case NSKeyUp: 323 324 if (_x_active) { 325 static BOOL do_swallow = NO; 326 static int swallow_keycode; 327 328 if ([e type] == NSKeyDown) { 329 /* Before that though, see if there are any global 330 * shortcuts bound to it. */ 331 332 if (darwinAppKitModMask &[e modifierFlags]) { 333 /* Override to force sending to Appkit */ 334 swallow_keycode = [e keyCode]; 335 do_swallow = YES; 336 for_x = NO; 337#if XPLUGIN_VERSION >= 1 338 } 339 else if (XQuartzEnableKeyEquivalents && 340 xp_is_symbolic_hotkey_event([e eventRef])) { 341 swallow_keycode = [e keyCode]; 342 do_swallow = YES; 343 for_x = NO; 344#endif 345 } 346 else if (XQuartzEnableKeyEquivalents && 347 [[self mainMenu] performKeyEquivalent:e]) { 348 swallow_keycode = [e keyCode]; 349 do_swallow = YES; 350 for_appkit = NO; 351 for_x = NO; 352 } 353 else if (!XQuartzIsRootless 354 && ([e modifierFlags] & ALL_KEY_MASKS) == 355 (NSCommandKeyMask | NSAlternateKeyMask) 356 && ([e keyCode] == 0 /*a*/ || [e keyCode] == 357 53 /*Esc*/)) { 358 /* We have this here to force processing fullscreen 359 * toggle even if XQuartzEnableKeyEquivalents is disabled */ 360 swallow_keycode = [e keyCode]; 361 do_swallow = YES; 362 for_x = NO; 363 for_appkit = NO; 364 DarwinSendDDXEvent(kXquartzToggleFullscreen, 0); 365 } 366 else { 367 /* No kit window is focused, so send it to X. */ 368 for_appkit = NO; 369 } 370 } 371 else { /* KeyUp */ 372 /* If we saw a key equivalent on the down, don't pass 373 * the up through to X. */ 374 if (do_swallow && [e keyCode] == swallow_keycode) { 375 do_swallow = NO; 376 for_x = NO; 377 } 378 } 379 } 380 else { /* !_x_active */ 381 for_x = NO; 382 } 383 break; 384 385 case NSFlagsChanged: 386 /* Don't tell X11 about modifiers changing while it's not active */ 387 if (!_x_active) 388 for_x = NO; 389 break; 390 391 case NSAppKitDefined: 392 switch ([e subtype]) { 393 static BOOL x_was_active = NO; 394 395 case NSApplicationActivatedEventType: 396 for_x = NO; 397 if ([e window] == nil && x_was_active) { 398 BOOL order_all_windows = YES, workspaces, ok; 399 for_appkit = NO; 400 401 /* FIXME: This is a hack to avoid passing the event to AppKit which 402 * would result in it raising one of its windows. 403 */ 404 _appFlags._active = YES; 405 406 [self set_front_process:nil]; 407 408 /* Get the Spaces preference for SwitchOnActivate */ 409 (void)CFPreferencesAppSynchronize(CFSTR("com.apple.dock")); 410 workspaces = 411 CFPreferencesGetAppBooleanValue(CFSTR("workspaces"), 412 CFSTR( 413 "com.apple.dock"), 414 &ok); 415 if (!ok) 416 workspaces = NO; 417 418 if (workspaces) { 419 (void)CFPreferencesAppSynchronize(CFSTR( 420 ".GlobalPreferences")); 421 order_all_windows = 422 CFPreferencesGetAppBooleanValue(CFSTR( 423 "AppleSpacesSwitchOnActivate"), 424 CFSTR( 425 ".GlobalPreferences"), 426 &ok); 427 if (!ok) 428 order_all_windows = YES; 429 } 430 431 /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered 432 * correctly, but we need to activate the top window on this space if there is 433 * none active. 434 * 435 * If there are no active windows, and there are minimized windows, we should 436 * be restoring one of them. 437 */ 438 if ([e data2] & 0x10) { // 0x10 (bfCPSOrderAllWindowsForward) is set when we use cmd-tab or the dock icon 439 DarwinSendDDXEvent(kXquartzBringAllToFront, 1, 440 order_all_windows); 441 } 442 } 443 break; 444 445 case 18: /* ApplicationDidReactivate */ 446 if (XQuartzFullscreenVisible) for_appkit = NO; 447 break; 448 449 case NSApplicationDeactivatedEventType: 450 for_x = NO; 451 452 x_was_active = _x_active; 453 if (_x_active) 454 [self activateX:NO]; 455 break; 456 } 457 break; 458 459 default: 460 break; /* for gcc */ 461 } 462 463 if (for_appkit) [super sendEvent:e]; 464 465 if (for_x) { 466#ifdef HAVE_LIBDISPATCH 467 dispatch_async(eventTranslationQueue, ^{ 468 [self sendX11NSEvent:e]; 469 }); 470#else 471 [self sendX11NSEvent:e]; 472#endif 473 } 474} 475 476- (void) set_window_menu:(NSArray *)list 477{ 478 [_controller set_window_menu:list]; 479} 480 481- (void) set_window_menu_check:(NSNumber *)n 482{ 483 [_controller set_window_menu_check:n]; 484} 485 486- (void) set_apps_menu:(NSArray *)list 487{ 488 [_controller set_apps_menu:list]; 489} 490 491- (void) set_front_process:unused 492{ 493 [NSApp activateIgnoringOtherApps:YES]; 494 495 if ([self modalWindow] == nil) 496 [self activateX:YES]; 497} 498 499- (void) set_can_quit:(NSNumber *)state 500{ 501 [_controller set_can_quit:[state boolValue]]; 502} 503 504- (void) server_ready:unused 505{ 506 [_controller server_ready]; 507} 508 509- (void) show_hide_menubar:(NSNumber *)state 510{ 511 /* Also shows/hides the dock */ 512 if ([state boolValue]) 513 SetSystemUIMode(kUIModeNormal, 0); 514 else 515 SetSystemUIMode(kUIModeAllHidden, 516 XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0); // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation" 517} 518 519- (void) launch_client:(NSString *)cmd 520{ 521 (void)[_controller application:self openFile:cmd]; 522} 523 524/* user preferences */ 525 526/* Note that these functions only work for arrays whose elements 527 can be toll-free-bridged between NS and CF worlds. */ 528 529static const void * 530cfretain(CFAllocatorRef a, const void *b) 531{ 532 return CFRetain(b); 533} 534 535static void 536cfrelease(CFAllocatorRef a, const void *b) 537{ 538 CFRelease(b); 539} 540 541CF_RETURNS_RETAINED 542static CFMutableArrayRef 543nsarray_to_cfarray(NSArray *in) 544{ 545 CFMutableArrayRef out; 546 CFArrayCallBacks cb; 547 NSObject *ns; 548 const CFTypeRef *cf; 549 int i, count; 550 551 memset(&cb, 0, sizeof(cb)); 552 cb.version = 0; 553 cb.retain = cfretain; 554 cb.release = cfrelease; 555 556 count = [in count]; 557 out = CFArrayCreateMutable(NULL, count, &cb); 558 559 for (i = 0; i < count; i++) { 560 ns = [in objectAtIndex:i]; 561 562 if ([ns isKindOfClass:[NSArray class]]) 563 cf = (CFTypeRef)nsarray_to_cfarray((NSArray *)ns); 564 else 565 cf = CFRetain((CFTypeRef)ns); 566 567 CFArrayAppendValue(out, cf); 568 CFRelease(cf); 569 } 570 571 return out; 572} 573 574static NSMutableArray * 575cfarray_to_nsarray(CFArrayRef in) 576{ 577 NSMutableArray *out; 578 const CFTypeRef *cf; 579 NSObject *ns; 580 int i, count; 581 582 count = CFArrayGetCount(in); 583 out = [[NSMutableArray alloc] initWithCapacity:count]; 584 585 for (i = 0; i < count; i++) { 586 cf = CFArrayGetValueAtIndex(in, i); 587 588 if (CFGetTypeID(cf) == CFArrayGetTypeID()) 589 ns = cfarray_to_nsarray((CFArrayRef)cf); 590 else 591 ns = [(id) cf retain]; 592 593 [out addObject:ns]; 594 [ns release]; 595 } 596 597 return out; 598} 599 600- (CFPropertyListRef) prefs_get_copy:(NSString *)key 601{ 602 CFPropertyListRef value; 603 604 value = CFPreferencesCopyAppValue((CFStringRef)key, 605 app_prefs_domain_cfstr); 606 607 if (value == NULL) { 608 static CFDictionaryRef defaults; 609 610 if (defaults == NULL) { 611 CFStringRef error = NULL; 612 CFDataRef data; 613 CFURLRef url; 614 SInt32 error_code; 615 616 url = (CFURLCreateFromFileSystemRepresentation 617 (NULL, (unsigned char *)DEFAULTS_FILE, 618 strlen(DEFAULTS_FILE), false)); 619 if (CFURLCreateDataAndPropertiesFromResource(NULL, url, &data, 620 NULL, NULL, 621 &error_code)) { 622 defaults = (CFPropertyListCreateFromXMLData 623 (NULL, data, 624 kCFPropertyListMutableContainersAndLeaves, 625 &error)); 626 if (error != NULL) CFRelease(error); 627 CFRelease(data); 628 } 629 CFRelease(url); 630 631 if (defaults != NULL) { 632 NSMutableArray *apps, *elt; 633 int count, i; 634 NSString *name, *nname; 635 636 /* Localize the names in the default apps menu. */ 637 638 apps = 639 [(NSDictionary *) defaults objectForKey:@PREFS_APPSMENU]; 640 if (apps != nil) { 641 count = [apps count]; 642 for (i = 0; i < count; i++) { 643 elt = [apps objectAtIndex:i]; 644 if (elt != nil && 645 [elt isKindOfClass:[NSArray class]]) { 646 name = [elt objectAtIndex:0]; 647 if (name != nil) { 648 nname = NSLocalizedString(name, nil); 649 if (nname != nil && nname != name) 650 [elt replaceObjectAtIndex:0 withObject: 651 nname]; 652 } 653 } 654 } 655 } 656 } 657 } 658 659 if (defaults != NULL) value = CFDictionaryGetValue(defaults, key); 660 if (value != NULL) CFRetain(value); 661 } 662 663 return value; 664} 665 666- (int) prefs_get_integer:(NSString *)key default:(int)def 667{ 668 CFPropertyListRef value; 669 int ret; 670 671 value = [self prefs_get_copy:key]; 672 673 if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID()) 674 CFNumberGetValue(value, kCFNumberIntType, &ret); 675 else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) 676 ret = CFStringGetIntValue(value); 677 else 678 ret = def; 679 680 if (value != NULL) CFRelease(value); 681 682 return ret; 683} 684 685- (const char *) prefs_get_string:(NSString *)key default:(const char *)def 686{ 687 CFPropertyListRef value; 688 const char *ret = NULL; 689 690 value = [self prefs_get_copy:key]; 691 692 if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) { 693 NSString *s = (NSString *)value; 694 695 ret = [s UTF8String]; 696 } 697 698 if (value != NULL) CFRelease(value); 699 700 return ret != NULL ? ret : def; 701} 702 703- (NSURL *) prefs_copy_url:(NSString *)key default:(NSURL *)def 704{ 705 CFPropertyListRef value; 706 NSURL *ret = NULL; 707 708 value = [self prefs_get_copy:key]; 709 710 if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) { 711 NSString *s = (NSString *)value; 712 713 ret = [NSURL URLWithString:s]; 714 [ret retain]; 715 } 716 717 if (value != NULL) CFRelease(value); 718 719 return ret != NULL ? ret : def; 720} 721 722- (float) prefs_get_float:(NSString *)key default:(float)def 723{ 724 CFPropertyListRef value; 725 float ret = def; 726 727 value = [self prefs_get_copy:key]; 728 729 if (value != NULL 730 && CFGetTypeID(value) == CFNumberGetTypeID() 731 && CFNumberIsFloatType(value)) 732 CFNumberGetValue(value, kCFNumberFloatType, &ret); 733 else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) 734 ret = CFStringGetDoubleValue(value); 735 736 if (value != NULL) CFRelease(value); 737 738 return ret; 739} 740 741- (int) prefs_get_boolean:(NSString *)key default:(int)def 742{ 743 CFPropertyListRef value; 744 int ret = def; 745 746 value = [self prefs_get_copy:key]; 747 748 if (value != NULL) { 749 if (CFGetTypeID(value) == CFNumberGetTypeID()) 750 CFNumberGetValue(value, kCFNumberIntType, &ret); 751 else if (CFGetTypeID(value) == CFBooleanGetTypeID()) 752 ret = CFBooleanGetValue(value); 753 else if (CFGetTypeID(value) == CFStringGetTypeID()) { 754 const char *tem = [(NSString *) value UTF8String]; 755 if (strcasecmp(tem, "true") == 0 || strcasecmp(tem, "yes") == 0) 756 ret = YES; 757 else 758 ret = NO; 759 } 760 761 CFRelease(value); 762 } 763 return ret; 764} 765 766- (NSArray *) prefs_get_array:(NSString *)key 767{ 768 NSArray *ret = nil; 769 CFPropertyListRef value; 770 771 value = [self prefs_get_copy:key]; 772 773 if (value != NULL) { 774 if (CFGetTypeID(value) == CFArrayGetTypeID()) 775 ret = [cfarray_to_nsarray (value)autorelease]; 776 777 CFRelease(value); 778 } 779 780 return ret; 781} 782 783- (void) prefs_set_integer:(NSString *)key value:(int)value 784{ 785 CFNumberRef x; 786 787 x = CFNumberCreate(NULL, kCFNumberIntType, &value); 788 789 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x, 790 app_prefs_domain_cfstr, 791 kCFPreferencesCurrentUser, 792 kCFPreferencesAnyHost); 793 794 CFRelease(x); 795} 796 797- (void) prefs_set_float:(NSString *)key value:(float)value 798{ 799 CFNumberRef x; 800 801 x = CFNumberCreate(NULL, kCFNumberFloatType, &value); 802 803 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x, 804 app_prefs_domain_cfstr, 805 kCFPreferencesCurrentUser, 806 kCFPreferencesAnyHost); 807 808 CFRelease(x); 809} 810 811- (void) prefs_set_boolean:(NSString *)key value:(int)value 812{ 813 CFPreferencesSetValue( 814 (CFStringRef)key, 815 (CFTypeRef)(value ? kCFBooleanTrue 816 : kCFBooleanFalse), 817 app_prefs_domain_cfstr, 818 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 819 820} 821 822- (void) prefs_set_array:(NSString *)key value:(NSArray *)value 823{ 824 CFArrayRef cfarray; 825 826 cfarray = nsarray_to_cfarray(value); 827 CFPreferencesSetValue((CFStringRef)key, 828 (CFTypeRef)cfarray, 829 app_prefs_domain_cfstr, 830 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 831 CFRelease(cfarray); 832} 833 834- (void) prefs_set_string:(NSString *)key value:(NSString *)value 835{ 836 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)value, 837 app_prefs_domain_cfstr, kCFPreferencesCurrentUser, 838 kCFPreferencesAnyHost); 839} 840 841- (void) prefs_synchronize 842{ 843 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 844} 845 846- (void) read_defaults 847{ 848 NSString *nsstr; 849 const char *tem; 850 851 XQuartzRootlessDefault = [self prefs_get_boolean:@PREFS_ROOTLESS 852 default :XQuartzRootlessDefault]; 853 XQuartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU 854 default :XQuartzFullscreenMenu]; 855 XQuartzFullscreenDisableHotkeys = 856 ![self prefs_get_boolean:@PREFS_FULLSCREEN_HOTKEYS 857 default :! 858 XQuartzFullscreenDisableHotkeys]; 859 darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS 860 default :darwinFakeButtons]; 861 XQuartzOptionSendsAlt = [self prefs_get_boolean:@PREFS_OPTION_SENDS_ALT 862 default :XQuartzOptionSendsAlt]; 863 864 if (darwinFakeButtons) { 865 const char *fake2, *fake3; 866 867 fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL]; 868 fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL]; 869 870 if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList( 871 fake2, TRUE); 872 if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList( 873 fake3, TRUE); 874 } 875 876 tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL]; 877 if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE); 878 879 tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL]; 880 if (tem != NULL) { 881 windowItemModMask = DarwinParseModifierList(tem, FALSE); 882 } 883 else { 884 nsstr = NSLocalizedString(@"window item modifiers", 885 @"window item modifiers"); 886 if (nsstr != NULL) { 887 tem = [nsstr UTF8String]; 888 if ((tem != NULL) && strcmp(tem, "window item modifiers")) { 889 windowItemModMask = DarwinParseModifierList(tem, FALSE); 890 } 891 } 892 } 893 894 XQuartzEnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS 895 default : 896 XQuartzEnableKeyEquivalents]; 897 898 darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP 899 default :darwinSyncKeymap]; 900 901 darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH 902 default :darwinDesiredDepth]; 903 904 noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS 905 default :FALSE]; 906 907 noRenderExtension = ![self prefs_get_boolean:@PREFS_RENDER_EXTENSION 908 default :TRUE]; 909 910 XQuartzScrollInDeviceDirection = 911 [self prefs_get_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION 912 default : 913 XQuartzScrollInDeviceDirection]; 914 915#if XQUARTZ_SPARKLE 916 NSURL *url = [self prefs_copy_url:@PREFS_UPDATE_FEED default:nil]; 917 if (url) { 918 [[SUUpdater sharedUpdater] setFeedURL:url]; 919 [url release]; 920 } 921#endif 922} 923 924/* This will end up at the end of the responder chain. */ 925- (void) copy:sender 926{ 927 DarwinSendDDXEvent(kXquartzPasteboardNotify, 1, 928 AppleWMCopyToPasteboard); 929} 930 931- (X11Controller *) controller 932{ 933 return _controller; 934} 935 936- (OSX_BOOL) x_active 937{ 938 return _x_active; 939} 940 941@end 942 943static NSArray * 944array_with_strings_and_numbers(int nitems, const char **items, 945 const char *numbers) 946{ 947 NSMutableArray *array, *subarray; 948 NSString *string, *number; 949 int i; 950 951 /* (Can't autorelease on the X server thread) */ 952 953 array = [[NSMutableArray alloc] initWithCapacity:nitems]; 954 955 for (i = 0; i < nitems; i++) { 956 subarray = [[NSMutableArray alloc] initWithCapacity:2]; 957 958 string = [[NSString alloc] initWithUTF8String:items[i]]; 959 [subarray addObject:string]; 960 [string release]; 961 962 if (numbers[i] != 0) { 963 number = [[NSString alloc] initWithFormat:@"%d", numbers[i]]; 964 [subarray addObject:number]; 965 [number release]; 966 } 967 else 968 [subarray addObject:@""]; 969 970 [array addObject:subarray]; 971 [subarray release]; 972 } 973 974 return array; 975} 976 977void 978X11ApplicationSetWindowMenu(int nitems, const char **items, 979 const char *shortcuts) 980{ 981 NSArray *array; 982 array = array_with_strings_and_numbers(nitems, items, shortcuts); 983 984 /* Send the array of strings over to the appkit thread */ 985 986 message_kit_thread(@selector (set_window_menu:), array); 987 [array release]; 988} 989 990void 991X11ApplicationSetWindowMenuCheck(int idx) 992{ 993 NSNumber *n; 994 995 n = [[NSNumber alloc] initWithInt:idx]; 996 997 message_kit_thread(@selector (set_window_menu_check:), n); 998 999 [n release]; 1000} 1001 1002void 1003X11ApplicationSetFrontProcess(void) 1004{ 1005 message_kit_thread(@selector (set_front_process:), nil); 1006} 1007 1008void 1009X11ApplicationSetCanQuit(int state) 1010{ 1011 NSNumber *n; 1012 1013 n = [[NSNumber alloc] initWithBool:state]; 1014 1015 message_kit_thread(@selector (set_can_quit:), n); 1016 1017 [n release]; 1018} 1019 1020void 1021X11ApplicationServerReady(void) 1022{ 1023 message_kit_thread(@selector (server_ready:), nil); 1024} 1025 1026void 1027X11ApplicationShowHideMenubar(int state) 1028{ 1029 NSNumber *n; 1030 1031 n = [[NSNumber alloc] initWithBool:state]; 1032 1033 message_kit_thread(@selector (show_hide_menubar:), n); 1034 1035 [n release]; 1036} 1037 1038void 1039X11ApplicationLaunchClient(const char *cmd) 1040{ 1041 NSString *string; 1042 1043 string = [[NSString alloc] initWithUTF8String:cmd]; 1044 1045 message_kit_thread(@selector (launch_client:), string); 1046 1047 [string release]; 1048} 1049 1050/* This is a special function in that it is run from the *SERVER* thread and 1051 * not the AppKit thread. We want to block entering a screen-capturing RandR 1052 * mode until we notify the user about how to get out if the X11 client crashes. 1053 */ 1054Bool 1055X11ApplicationCanEnterRandR(void) 1056{ 1057 NSString *title, *msg; 1058 1059 if ([X11App prefs_get_boolean:@PREFS_NO_RANDR_ALERT default:NO] || 1060 XQuartzShieldingWindowLevel != 0) 1061 return TRUE; 1062 1063 title = NSLocalizedString(@"Enter RandR mode?", 1064 @"Dialog title when switching to RandR"); 1065 msg = NSLocalizedString( 1066 @"An application has requested X11 to change the resolution of your display. X11 will restore the display to its previous state when the requesting application requests to return to the previous state. Alternatively, you can use the ⌥⌘A key sequence to force X11 to return to the previous state.", 1067 @"Dialog when switching to RandR"); 1068 1069 if (!XQuartzIsRootless) 1070 QuartzShowFullscreen(FALSE); 1071 1072 switch (NSRunAlertPanel(title, @"%@", 1073 NSLocalizedString(@"Allow", 1074 @""), 1075 NSLocalizedString(@"Cancel", 1076 @""), 1077 NSLocalizedString(@"Always Allow", @""), msg)) { 1078 case NSAlertOtherReturn: 1079 [X11App prefs_set_boolean:@PREFS_NO_RANDR_ALERT value:YES]; 1080 [X11App prefs_synchronize]; 1081 1082 case NSAlertDefaultReturn: 1083 return YES; 1084 1085 default: 1086 return NO; 1087 } 1088} 1089 1090void 1091X11ApplicationFatalError(const char *f, va_list args) 1092{ 1093#ifdef HAVE_LIBDISPATCH 1094 NSString *title, *msg; 1095 char *error_msg; 1096 1097 /* This is called by FatalError() in the server thread just before 1098 * we would abort. If the server never got off the ground, We should 1099 * inform the user of the error rather than letting the ever-so-friendly 1100 * CrashReporter do it for us. 1101 * 1102 * This also has the benefit of forcing user interaction rather than 1103 * allowing an infinite throttled-restart if the crash occurs before 1104 * we can drain the launchd socket. 1105 */ 1106 1107 if (serverRunning) { 1108 return; 1109 } 1110 1111 title = NSLocalizedString(@"The application X11 could not be opened.", 1112 @"Dialog title when encountering a fatal error"); 1113 msg = NSLocalizedString( 1114 @"An error occurred while starting the X11 server: \"%s\"\n\nClick Quit to quit X11. Click Report to see more details or send a report to Apple.", 1115 @"Dialog when encountering a fatal error"); 1116 1117 vasprintf(&error_msg, f, args); 1118 msg = [NSString stringWithFormat:msg, error_msg]; 1119 1120 /* We want the AppKit thread to actually service the alert or we will race [NSApp run] and create an 1121 * 'NSInternalInconsistencyException', reason: 'NSApp with wrong _running count' 1122 */ 1123 dispatch_sync(dispatch_get_main_queue(), ^{ 1124 if (NSAlertDefaultReturn == 1125 NSRunAlertPanel (title, @"%@", 1126 NSLocalizedString (@"Quit", @""), 1127 NSLocalizedString (@"Report...", @""), 1128 nil, msg)) { 1129 exit (EXIT_FAILURE); 1130 } 1131 }); 1132 1133 /* fall back to caller to do the abort() in the DIX */ 1134#endif 1135} 1136 1137static void 1138check_xinitrc(void) 1139{ 1140 char *tem, buf[1024]; 1141 NSString *msg; 1142 1143 if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO]) 1144 return; 1145 1146 tem = getenv("HOME"); 1147 if (tem == NULL) goto done; 1148 1149 snprintf(buf, sizeof(buf), "%s/.xinitrc", tem); 1150 if (access(buf, F_OK) != 0) 1151 goto done; 1152 1153 msg = 1154 NSLocalizedString( 1155 @"You have an existing ~/.xinitrc file.\n\n\ 1156 Windows displayed by X11 applications may not have titlebars, or may look \ 1157 different to windows displayed by native applications.\n\n\ 1158 Would you like to move aside the existing file and use the standard X11 \ 1159 environment the next time you start X11?" , 1160 @"Startup xinitrc dialog"); 1161 1162 if (NSAlertDefaultReturn == 1163 NSRunAlertPanel(nil, @"%@", NSLocalizedString(@"Yes", @""), 1164 NSLocalizedString(@"No", @""), nil, msg)) { 1165 char buf2[1024]; 1166 int i = -1; 1167 1168 snprintf(buf2, sizeof(buf2), "%s.old", buf); 1169 1170 for (i = 1; access(buf2, F_OK) == 0; i++) 1171 snprintf(buf2, sizeof(buf2), "%s.old.%d", buf, i); 1172 1173 rename(buf, buf2); 1174 } 1175 1176done: 1177 [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES]; 1178 [X11App prefs_synchronize]; 1179} 1180 1181static inline pthread_t 1182create_thread(void *(*func)(void *), void *arg) 1183{ 1184 pthread_attr_t attr; 1185 pthread_t tid; 1186 1187 pthread_attr_init(&attr); 1188 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 1189 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1190 pthread_create(&tid, &attr, func, arg); 1191 pthread_attr_destroy(&attr); 1192 1193 return tid; 1194} 1195 1196static void * 1197xpbproxy_x_thread(void *args) 1198{ 1199 xpbproxy_run(); 1200 1201 ErrorF("xpbproxy thread is terminating unexpectedly.\n"); 1202 return NULL; 1203} 1204 1205void 1206X11ApplicationMain(int argc, char **argv, char **envp) 1207{ 1208 NSAutoreleasePool *pool; 1209 1210#ifdef DEBUG 1211 while (access("/tmp/x11-block", F_OK) == 0) sleep(1); 1212#endif 1213 1214 pool = [[NSAutoreleasePool alloc] init]; 1215 X11App = (X11Application *)[X11Application sharedApplication]; 1216 init_ports(); 1217 1218 app_prefs_domain_cfstr = 1219 (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]; 1220 1221 if (app_prefs_domain_cfstr == NULL) { 1222 ErrorF( 1223 "X11ApplicationMain: Unable to determine bundle identifier. Your installation of XQuartz may be broken.\n"); 1224 app_prefs_domain_cfstr = CFSTR(BUNDLE_ID_PREFIX ".X11"); 1225 } 1226 1227 [NSApp read_defaults]; 1228 [NSBundle loadNibNamed:@"main" owner:NSApp]; 1229 [[NSNotificationCenter defaultCenter] addObserver:NSApp 1230 selector:@selector (became_key:) 1231 name: 1232 NSWindowDidBecomeKeyNotification object:nil]; 1233 1234 /* 1235 * The xpr Quartz mode is statically linked into this server. 1236 * Initialize all the Quartz functions. 1237 */ 1238 QuartzModeBundleInit(); 1239 1240 /* Calculate the height of the menubar so we can avoid it. */ 1241 aquaMenuBarHeight = [[NSApp mainMenu] menuBarHeight]; 1242#if ! __LP64__ 1243 if (!aquaMenuBarHeight) { 1244 aquaMenuBarHeight = [NSMenuView menuBarHeight]; 1245 } 1246#endif 1247 if (!aquaMenuBarHeight) { 1248 NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0]; 1249 aquaMenuBarHeight = NSHeight([primaryScreen frame]) - NSMaxY([primaryScreen visibleFrame]); 1250 } 1251 1252#ifdef HAVE_LIBDISPATCH 1253 eventTranslationQueue = dispatch_queue_create( 1254 BUNDLE_ID_PREFIX ".X11.NSEventsToX11EventsQueue", NULL); 1255 assert(eventTranslationQueue != NULL); 1256#endif 1257 1258 /* Set the key layout seed before we start the server */ 1259#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 1260 last_key_layout = TISCopyCurrentKeyboardLayoutInputSource(); 1261 1262 if (!last_key_layout) 1263 ErrorF( 1264 "X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n"); 1265#else 1266 KLGetCurrentKeyboardLayout(&last_key_layout); 1267 if (!last_key_layout) 1268 ErrorF( 1269 "X11ApplicationMain: Unable to determine KLGetCurrentKeyboardLayout() at startup.\n"); 1270#endif 1271 1272 if (!QuartsResyncKeymap(FALSE)) { 1273 ErrorF("X11ApplicationMain: Could not build a valid keymap.\n"); 1274 } 1275 1276 /* Tell the server thread that it can proceed */ 1277 QuartzInitServer(argc, argv, envp); 1278 1279 /* This must be done after QuartzInitServer because it can result in 1280 * an mieqEnqueue() - <rdar://problem/6300249> 1281 */ 1282 check_xinitrc(); 1283 1284 create_thread(xpbproxy_x_thread, NULL); 1285 1286#if XQUARTZ_SPARKLE 1287 [[X11App controller] setup_sparkle]; 1288 [[SUUpdater sharedUpdater] resetUpdateCycle]; 1289 // [[SUUpdater sharedUpdater] checkForUpdates:X11App]; 1290#endif 1291 1292 [pool release]; 1293 [NSApp run]; 1294 /* not reached */ 1295} 1296 1297@implementation X11Application (Private) 1298 1299#ifdef NX_DEVICELCMDKEYMASK 1300/* This is to workaround a bug in the VNC server where we sometimes see the L 1301 * modifier and sometimes see no "side" 1302 */ 1303static inline int 1304ensure_flag(int flags, int device_independent, int device_dependents, 1305 int device_dependent_default) 1306{ 1307 if ((flags & device_independent) && 1308 !(flags & device_dependents)) 1309 flags |= device_dependent_default; 1310 return flags; 1311} 1312#endif 1313 1314#ifdef DEBUG_UNTRUSTED_POINTER_DELTA 1315static const char * 1316untrusted_str(NSEvent *e) 1317{ 1318 switch ([e type]) { 1319 case NSScrollWheel: 1320 return "NSScrollWheel"; 1321 1322 case NSTabletPoint: 1323 return "NSTabletPoint"; 1324 1325 case NSOtherMouseDown: 1326 return "NSOtherMouseDown"; 1327 1328 case NSOtherMouseUp: 1329 return "NSOtherMouseUp"; 1330 1331 case NSLeftMouseDown: 1332 return "NSLeftMouseDown"; 1333 1334 case NSLeftMouseUp: 1335 return "NSLeftMouseUp"; 1336 1337 default: 1338 switch ([e subtype]) { 1339 case NSTabletPointEventSubtype: 1340 return "NSTabletPointEventSubtype"; 1341 1342 case NSTabletProximityEventSubtype: 1343 return "NSTabletProximityEventSubtype"; 1344 1345 default: 1346 return "Other"; 1347 } 1348 } 1349} 1350#endif 1351 1352extern void 1353darwinEvents_lock(void); 1354extern void 1355darwinEvents_unlock(void); 1356 1357- (void) sendX11NSEvent:(NSEvent *)e 1358{ 1359 NSPoint location = NSZeroPoint; 1360 int ev_button, ev_type; 1361 static float pressure = 0.0; // static so ProximityOut will have the value from the previous tablet event 1362 static NSPoint tilt; // static so ProximityOut will have the value from the previous tablet event 1363 static DeviceIntPtr darwinTabletCurrent = NULL; 1364 static BOOL needsProximityIn = NO; // Do we do need to handle a pending ProximityIn once we have pressure/tilt? 1365 DeviceIntPtr pDev; 1366 int modifierFlags; 1367 BOOL isMouseOrTabletEvent, isTabletEvent; 1368 1369 if (!darwinTabletCurrent) { 1370 /* Ensure that the event system is initialized */ 1371 darwinEvents_lock(); 1372 darwinEvents_unlock(); 1373 assert(darwinTabletStylus); 1374 1375 tilt = NSZeroPoint; 1376 darwinTabletCurrent = darwinTabletStylus; 1377 } 1378 1379 isMouseOrTabletEvent = [e type] == NSLeftMouseDown || 1380 [e type] == NSOtherMouseDown || 1381 [e type] == NSRightMouseDown || 1382 [e type] == NSLeftMouseUp || 1383 [e type] == NSOtherMouseUp || 1384 [e type] == NSRightMouseUp || 1385 [e type] == NSLeftMouseDragged || 1386 [e type] == NSOtherMouseDragged || 1387 [e type] == NSRightMouseDragged || 1388 [e type] == NSMouseMoved || 1389 [e type] == NSTabletPoint || 1390 [e type] == NSScrollWheel; 1391 1392 isTabletEvent = ([e type] == NSTabletPoint) || 1393 (isMouseOrTabletEvent && 1394 ([e subtype] == NSTabletPointEventSubtype || 1395 [e subtype] == NSTabletProximityEventSubtype)); 1396 1397 if (isMouseOrTabletEvent) { 1398 static NSPoint lastpt; 1399 NSWindow *window = [e window]; 1400 NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame]; 1401 BOOL hasUntrustedPointerDelta; 1402 1403 // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that 1404 // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets 1405 // are not normally used in cases where that bug would present itself, so this is a fair tradeoff 1406 // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype 1407 // http://xquartz.macosforge.org/trac/ticket/288 1408 hasUntrustedPointerDelta = isTabletEvent; 1409 1410 // The deltaXY for middle click events also appear erroneous after fast user switching 1411 // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS 1412 // http://xquartz.macosforge.org/trac/ticket/389 1413 hasUntrustedPointerDelta |= [e type] == NSOtherMouseDown || 1414 [e type] == NSOtherMouseUp; 1415 1416 // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta 1417 // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement 1418 hasUntrustedPointerDelta |= [e type] == NSScrollWheel; 1419 1420#ifdef DEBUG_UNTRUSTED_POINTER_DELTA 1421 hasUntrustedPointerDelta |= [e type] == NSLeftMouseDown || 1422 [e type] == NSLeftMouseUp; 1423#endif 1424 1425 if (window != nil) { 1426 NSRect frame = [window frame]; 1427 location = [e locationInWindow]; 1428 location.x += frame.origin.x; 1429 location.y += frame.origin.y; 1430 lastpt = location; 1431 } 1432 else if (hasUntrustedPointerDelta) { 1433#ifdef DEBUG_UNTRUSTED_POINTER_DELTA 1434 ErrorF("--- Begin Event Debug ---\n"); 1435 ErrorF("Event type: %s\n", untrusted_str(e)); 1436 ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y); 1437 ErrorF(" delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]); 1438 ErrorF(" location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX], 1439 lastpt.y - [e deltaY]); 1440 ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x, 1441 [e locationInWindow].y); 1442 ErrorF("--- End Event Debug ---\n"); 1443 1444 location.x = lastpt.x + [e deltaX]; 1445 location.y = lastpt.y - [e deltaY]; 1446 lastpt = [e locationInWindow]; 1447#else 1448 location = [e locationInWindow]; 1449 lastpt = location; 1450#endif 1451 } 1452 else { 1453 location.x = lastpt.x + [e deltaX]; 1454 location.y = lastpt.y - [e deltaY]; 1455 lastpt = [e locationInWindow]; 1456 } 1457 1458 /* Convert coordinate system */ 1459 location.y = (screen.origin.y + screen.size.height) - location.y; 1460 } 1461 1462 modifierFlags = [e modifierFlags]; 1463 1464#ifdef NX_DEVICELCMDKEYMASK 1465 /* This is to workaround a bug in the VNC server where we sometimes see the L 1466 * modifier and sometimes see no "side" 1467 */ 1468 modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK, 1469 NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK, 1470 NX_DEVICELCTLKEYMASK); 1471 modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK, 1472 NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK, 1473 NX_DEVICELSHIFTKEYMASK); 1474 modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK, 1475 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK, 1476 NX_DEVICELCMDKEYMASK); 1477 modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK, 1478 NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK, 1479 NX_DEVICELALTKEYMASK); 1480#endif 1481 1482 modifierFlags &= darwin_all_modifier_mask; 1483 1484 /* We don't receive modifier key events while out of focus, and 3button 1485 * emulation mucks this up, so we need to check our modifier flag state 1486 * on every event... ugg 1487 */ 1488 1489 if (darwin_all_modifier_flags != modifierFlags) 1490 DarwinUpdateModKeys(modifierFlags); 1491 1492 switch ([e type]) { 1493 case NSLeftMouseDown: 1494 ev_button = 1; 1495 ev_type = ButtonPress; 1496 goto handle_mouse; 1497 1498 case NSOtherMouseDown: 1499 ev_button = 2; 1500 ev_type = ButtonPress; 1501 goto handle_mouse; 1502 1503 case NSRightMouseDown: 1504 ev_button = 3; 1505 ev_type = ButtonPress; 1506 goto handle_mouse; 1507 1508 case NSLeftMouseUp: 1509 ev_button = 1; 1510 ev_type = ButtonRelease; 1511 goto handle_mouse; 1512 1513 case NSOtherMouseUp: 1514 ev_button = 2; 1515 ev_type = ButtonRelease; 1516 goto handle_mouse; 1517 1518 case NSRightMouseUp: 1519 ev_button = 3; 1520 ev_type = ButtonRelease; 1521 goto handle_mouse; 1522 1523 case NSLeftMouseDragged: 1524 ev_button = 1; 1525 ev_type = MotionNotify; 1526 goto handle_mouse; 1527 1528 case NSOtherMouseDragged: 1529 ev_button = 2; 1530 ev_type = MotionNotify; 1531 goto handle_mouse; 1532 1533 case NSRightMouseDragged: 1534 ev_button = 3; 1535 ev_type = MotionNotify; 1536 goto handle_mouse; 1537 1538 case NSMouseMoved: 1539 ev_button = 0; 1540 ev_type = MotionNotify; 1541 goto handle_mouse; 1542 1543 case NSTabletPoint: 1544 ev_button = 0; 1545 ev_type = MotionNotify; 1546 goto handle_mouse; 1547 1548handle_mouse: 1549 pDev = darwinPointer; 1550 1551 /* NSTabletPoint can have no subtype */ 1552 if ([e type] != NSTabletPoint && 1553 [e subtype] == NSTabletProximityEventSubtype) { 1554 switch ([e pointingDeviceType]) { 1555 case NSEraserPointingDevice: 1556 darwinTabletCurrent = darwinTabletEraser; 1557 break; 1558 1559 case NSPenPointingDevice: 1560 darwinTabletCurrent = darwinTabletStylus; 1561 break; 1562 1563 case NSCursorPointingDevice: 1564 case NSUnknownPointingDevice: 1565 default: 1566 darwinTabletCurrent = darwinTabletCursor; 1567 break; 1568 } 1569 1570 if ([e isEnteringProximity]) 1571 needsProximityIn = YES; 1572 else 1573 DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0, 1574 location.x, location.y, pressure, 1575 tilt.x, tilt.y); 1576 return; 1577 } 1578 1579 if ([e type] == NSTabletPoint || 1580 [e subtype] == NSTabletPointEventSubtype) { 1581 pressure = [e pressure]; 1582 tilt = [e tilt]; 1583 1584 pDev = darwinTabletCurrent; 1585 1586 if (needsProximityIn) { 1587 DarwinSendTabletEvents(darwinTabletCurrent, ProximityIn, 0, 1588 location.x, location.y, pressure, 1589 tilt.x, tilt.y); 1590 1591 needsProximityIn = NO; 1592 } 1593 } 1594 1595 if (!XQuartzServerVisible && noTestExtensions) { 1596#if defined(XPLUGIN_VERSION) && XPLUGIN_VERSION > 0 1597 /* Older libXplugin (Tiger/"Stock" Leopard) aren't thread safe, so we can't call xp_find_window from the Appkit thread */ 1598 xp_window_id wid = 0; 1599 xp_error err; 1600 1601 /* Sigh. Need to check that we're really over one of 1602 * our windows. (We need to receive pointer events while 1603 * not in the foreground, but we don't want to receive them 1604 * when another window is over us or we might show a tooltip) 1605 */ 1606 1607 err = xp_find_window(location.x, location.y, 0, &wid); 1608 1609 if (err != XP_Success || (err == XP_Success && wid == 0)) 1610#endif 1611 { 1612 bgMouseLocation = location; 1613 bgMouseLocationUpdated = TRUE; 1614 return; 1615 } 1616 } 1617 1618 if (bgMouseLocationUpdated) { 1619 if (!(ev_type == MotionNotify && ev_button == 0)) { 1620 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0, 1621 location.x, location.y, 1622 0.0, 0.0); 1623 } 1624 bgMouseLocationUpdated = FALSE; 1625 } 1626 1627 if (pDev == darwinPointer) { 1628 DarwinSendPointerEvents(pDev, ev_type, ev_button, 1629 location.x, location.y, 1630 [e deltaX], [e deltaY]); 1631 } else { 1632 DarwinSendTabletEvents(pDev, ev_type, ev_button, 1633 location.x, location.y, pressure, 1634 tilt.x, tilt.y); 1635 } 1636 1637 break; 1638 1639 case NSTabletProximity: 1640 switch ([e pointingDeviceType]) { 1641 case NSEraserPointingDevice: 1642 darwinTabletCurrent = darwinTabletEraser; 1643 break; 1644 1645 case NSPenPointingDevice: 1646 darwinTabletCurrent = darwinTabletStylus; 1647 break; 1648 1649 case NSCursorPointingDevice: 1650 case NSUnknownPointingDevice: 1651 default: 1652 darwinTabletCurrent = darwinTabletCursor; 1653 break; 1654 } 1655 1656 if ([e isEnteringProximity]) 1657 needsProximityIn = YES; 1658 else 1659 DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0, 1660 location.x, location.y, pressure, 1661 tilt.x, tilt.y); 1662 break; 1663 1664 case NSScrollWheel: 1665 { 1666#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 1667 float deltaX = [e deltaX]; 1668 float deltaY = [e deltaY]; 1669 BOOL isContinuous = NO; 1670#else 1671 CGFloat deltaX = [e deltaX]; 1672 CGFloat deltaY = [e deltaY]; 1673 CGEventRef cge = [e CGEvent]; 1674 BOOL isContinuous = 1675 CGEventGetIntegerValueField(cge, kCGScrollWheelEventIsContinuous); 1676 1677#if 0 1678 /* Scale the scroll value by line height */ 1679 CGEventSourceRef source = CGEventCreateSourceFromEvent(cge); 1680 if (source) { 1681 double lineHeight = CGEventSourceGetPixelsPerLine(source); 1682 CFRelease(source); 1683 1684 /* There's no real reason for the 1/5 ratio here other than that 1685 * it feels like a good ratio after some testing. 1686 */ 1687 1688 deltaX *= lineHeight / 5.0; 1689 deltaY *= lineHeight / 5.0; 1690 } 1691#endif 1692#endif 1693 1694#if !defined(XPLUGIN_VERSION) || XPLUGIN_VERSION == 0 1695 /* If we're in the background, we need to send a MotionNotify event 1696 * first, since we aren't getting them on background mouse motion 1697 */ 1698 if (!XQuartzServerVisible && noTestExtensions) { 1699 bgMouseLocationUpdated = FALSE; 1700 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0, 1701 location.x, location.y, 1702 0.0, 0.0); 1703 } 1704#endif 1705#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 1706 // TODO: Change 1117 to NSAppKitVersionNumber10_7 when it is defined 1707 if (NSAppKitVersionNumber >= 1117 && 1708 XQuartzScrollInDeviceDirection && 1709 [e isDirectionInvertedFromDevice]) { 1710 deltaX *= -1; 1711 deltaY *= -1; 1712 } 1713#endif 1714 /* This hack is in place to better deal with "clicky" scroll wheels: 1715 * http://xquartz.macosforge.org/trac/ticket/562 1716 */ 1717 if (!isContinuous) { 1718 static NSTimeInterval lastScrollTime = 0.0; 1719 1720 /* These store how much extra we have already scrolled. 1721 * ie, this is how much we ignore on the next event. 1722 */ 1723 static double deficit_x = 0.0; 1724 static double deficit_y = 0.0; 1725 1726 /* If we have past a second since the last scroll, wipe the slate 1727 * clean 1728 */ 1729 if ([e timestamp] - lastScrollTime > 1.0) { 1730 deficit_x = deficit_y = 0.0; 1731 } 1732 lastScrollTime = [e timestamp]; 1733 1734 if (deltaX != 0.0) { 1735 /* If we changed directions, wipe the slate clean */ 1736 if ((deficit_x < 0.0 && deltaX > 0.0) || 1737 (deficit_x > 0.0 && deltaX < 0.0)) { 1738 deficit_x = 0.0; 1739 } 1740 1741 /* Eat up the deficit, but ensure that something is 1742 * always sent 1743 */ 1744 if (fabs(deltaX) > fabs(deficit_x)) { 1745 deltaX -= deficit_x; 1746 1747 if (deltaX > 0.0) { 1748 deficit_x = ceil(deltaX) - deltaX; 1749 deltaX = ceil(deltaX); 1750 } else { 1751 deficit_x = floor(deltaX) - deltaX; 1752 deltaX = floor(deltaX); 1753 } 1754 } else { 1755 deficit_x -= deltaX; 1756 1757 if (deltaX > 0.0) { 1758 deltaX = 1.0; 1759 } else { 1760 deltaX = -1.0; 1761 } 1762 1763 deficit_x += deltaX; 1764 } 1765 } 1766 1767 if (deltaY != 0.0) { 1768 /* If we changed directions, wipe the slate clean */ 1769 if ((deficit_y < 0.0 && deltaY > 0.0) || 1770 (deficit_y > 0.0 && deltaY < 0.0)) { 1771 deficit_y = 0.0; 1772 } 1773 1774 /* Eat up the deficit, but ensure that something is 1775 * always sent 1776 */ 1777 if (fabs(deltaY) > fabs(deficit_y)) { 1778 deltaY -= deficit_y; 1779 1780 if (deltaY > 0.0) { 1781 deficit_y = ceil(deltaY) - deltaY; 1782 deltaY = ceil(deltaY); 1783 } else { 1784 deficit_y = floor(deltaY) - deltaY; 1785 deltaY = floor(deltaY); 1786 } 1787 } else { 1788 deficit_y -= deltaY; 1789 1790 if (deltaY > 0.0) { 1791 deltaY = 1.0; 1792 } else { 1793 deltaY = -1.0; 1794 } 1795 1796 deficit_y += deltaY; 1797 } 1798 } 1799 } 1800 1801 DarwinSendScrollEvents(deltaX, deltaY); 1802 break; 1803 } 1804 1805 case NSKeyDown: 1806 case NSKeyUp: 1807 { 1808 /* XKB clobbers our keymap at startup, so we need to force it on the first keypress. 1809 * TODO: Make this less of a kludge. 1810 */ 1811 static int force_resync_keymap = YES; 1812 if (force_resync_keymap) { 1813 DarwinSendDDXEvent(kXquartzReloadKeymap, 0); 1814 force_resync_keymap = NO; 1815 } 1816 } 1817 1818 if (darwinSyncKeymap) { 1819#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 1820 TISInputSourceRef key_layout = 1821 TISCopyCurrentKeyboardLayoutInputSource(); 1822 TISInputSourceRef clear; 1823 if (CFEqual(key_layout, last_key_layout)) { 1824 CFRelease(key_layout); 1825 } 1826 else { 1827 /* Swap/free thread-safely */ 1828 clear = last_key_layout; 1829 last_key_layout = key_layout; 1830 CFRelease(clear); 1831#else 1832 KeyboardLayoutRef key_layout; 1833 KLGetCurrentKeyboardLayout(&key_layout); 1834 if (key_layout != last_key_layout) { 1835 last_key_layout = key_layout; 1836#endif 1837 /* Update keyInfo */ 1838 if (!QuartsResyncKeymap(TRUE)) { 1839 ErrorF( 1840 "sendX11NSEvent: Could not build a valid keymap.\n"); 1841 } 1842 } 1843 } 1844 1845 ev_type = ([e type] == NSKeyDown) ? KeyPress : KeyRelease; 1846 DarwinSendKeyboardEvents(ev_type, [e keyCode]); 1847 break; 1848 1849 default: 1850 break; /* for gcc */ 1851 } 1852} 1853@end 1854