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