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