X11Application.m revision 4642e01f
1/* X11Application.m -- subclass of NSApplication to multiplex events 2 3 Copyright (c) 2002-2008 Apple Inc. 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#include "sanitizedCarbon.h" 31 32#ifdef HAVE_DIX_CONFIG_H 33#include <dix-config.h> 34#endif 35 36#include "quartzCommon.h" 37 38#import "X11Application.h" 39 40#include "darwin.h" 41#include "darwinEvents.h" 42#include "quartzKeyboard.h" 43#include "quartz.h" 44#define _APPLEWM_SERVER_ 45#include "X11/extensions/applewm.h" 46#include "micmap.h" 47 48#include <mach/mach.h> 49#include <unistd.h> 50#include <AvailabilityMacros.h> 51 52#include <Xplugin.h> 53 54// pbproxy/pbproxy.h 55extern BOOL xpbproxy_init (void); 56 57#define DEFAULTS_FILE X11LIBDIR"/X11/xserver/Xquartz.plist" 58 59#ifndef XSERVER_VERSION 60#define XSERVER_VERSION "?" 61#endif 62 63#define ProximityIn 0 64#define ProximityOut 1 65 66/* Stuck modifier / button state... force release when we context switch */ 67static NSEventType keyState[NUM_KEYCODES]; 68static int modifierFlagsMask; 69 70int X11EnableKeyEquivalents = TRUE, quartzFullscreenMenu = FALSE; 71int quartzHasRoot = FALSE, quartzEnableRootless = TRUE; 72 73extern Bool noTestExtensions; 74 75#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 76static TISInputSourceRef last_key_layout; 77#else 78static KeyboardLayoutRef last_key_layout; 79#endif 80 81extern int darwinFakeButtons; 82 83X11Application *X11App; 84 85CFStringRef app_prefs_domain_cfstr = NULL; 86 87#define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask) 88 89@interface X11Application (Private) 90- (void) sendX11NSEvent:(NSEvent *)e; 91@end 92 93@implementation X11Application 94 95typedef struct message_struct message; 96struct message_struct { 97 mach_msg_header_t hdr; 98 SEL selector; 99 NSObject *arg; 100}; 101 102static mach_port_t _port; 103 104/* Quartz mode initialization routine. This is often dynamically loaded 105 but is statically linked into this X server. */ 106Bool QuartzModeBundleInit(void); 107 108static void init_ports (void) { 109 kern_return_t r; 110 NSPort *p; 111 112 if (_port != MACH_PORT_NULL) return; 113 114 r = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &_port); 115 if (r != KERN_SUCCESS) return; 116 117 p = [NSMachPort portWithMachPort:_port]; 118 [p setDelegate:NSApp]; 119 [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 120} 121 122static void message_kit_thread (SEL selector, NSObject *arg) { 123 message msg; 124 kern_return_t r; 125 126 msg.hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0); 127 msg.hdr.msgh_size = sizeof (msg); 128 msg.hdr.msgh_remote_port = _port; 129 msg.hdr.msgh_local_port = MACH_PORT_NULL; 130 msg.hdr.msgh_reserved = 0; 131 msg.hdr.msgh_id = 0; 132 133 msg.selector = selector; 134 msg.arg = [arg retain]; 135 136 r = mach_msg (&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size, 137 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 138 if (r != KERN_SUCCESS) 139 ErrorF("%s: mach_msg failed: %x\n", __FUNCTION__, r); 140} 141 142- (void) handleMachMessage:(void *)_msg { 143 message *msg = _msg; 144 145 [self performSelector:msg->selector withObject:msg->arg]; 146 [msg->arg release]; 147} 148 149- (void) set_controller:obj { 150 if (_controller == nil) _controller = [obj retain]; 151} 152 153- (void) dealloc { 154 if (_controller != nil) [_controller release]; 155 156 if (_port != MACH_PORT_NULL) 157 mach_port_deallocate (mach_task_self (), _port); 158 159 [super dealloc]; 160} 161 162- (void) orderFrontStandardAboutPanel: (id) sender { 163 NSMutableDictionary *dict; 164 NSDictionary *infoDict; 165 NSString *tem; 166 167 dict = [NSMutableDictionary dictionaryWithCapacity:3]; 168 infoDict = [[NSBundle mainBundle] infoDictionary]; 169 170 [dict setObject: NSLocalizedString (@"The X Window System", @"About panel") 171 forKey:@"ApplicationName"]; 172 173 tem = [infoDict objectForKey:@"CFBundleShortVersionString"]; 174 175 [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem] 176 forKey:@"ApplicationVersion"]; 177 178 [dict setObject:[NSString stringWithFormat:@"xorg-server %s", XSERVER_VERSION] 179 forKey:@"Version"]; 180 181 [self orderFrontStandardAboutPanelWithOptions: dict]; 182} 183 184- (void) activateX:(OSX_BOOL)state { 185 /* Create a TSM document that supports full Unicode input, and 186 have it activated while X is active */ 187 static TSMDocumentID x11_document; 188 size_t i; 189 DEBUG_LOG("state=%d, _x_active=%d, \n", state, _x_active) 190 if (state) { 191 DarwinSendDDXEvent(kXquartzActivate, 0); 192 193 if (!_x_active) { 194 if (x11_document == 0) { 195 OSType types[1]; 196 types[0] = kUnicodeDocument; 197 NewTSMDocument (1, types, &x11_document, 0); 198 } 199 200 if (x11_document != 0) ActivateTSMDocument (x11_document); 201 } 202 } else { 203 204 if(darwin_modifier_flags) 205 DarwinUpdateModKeys(0); 206 for(i=0; i < NUM_KEYCODES; i++) { 207 if(keyState[i] == NSKeyDown) { 208 DarwinSendKeyboardEvents(KeyRelease, i); 209 keyState[i] = NSKeyUp; 210 } 211 } 212 213 DarwinSendDDXEvent(kXquartzDeactivate, 0); 214 215 if (_x_active && x11_document != 0) 216 DeactivateTSMDocument (x11_document); 217 } 218 219 _x_active = state; 220} 221 222- (void) became_key:(NSWindow *)win { 223 [self activateX:NO]; 224} 225 226- (void) sendEvent:(NSEvent *)e { 227 OSX_BOOL for_appkit, for_x; 228 229 /* By default pass down the responder chain and to X. */ 230 for_appkit = YES; 231 for_x = YES; 232 233 switch ([e type]) { 234 case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: 235 case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: 236 if ([e window] != nil) { 237 /* Pointer event has an (AppKit) window. Probably something for the kit. */ 238 for_x = NO; 239 if (_x_active) [self activateX:NO]; 240 } else if ([self modalWindow] == nil) { 241 /* Must be an X window. Tell appkit it doesn't have focus. */ 242 for_appkit = NO; 243 244 if ([self isActive]) { 245 [self deactivate]; 246 if (!_x_active && quartzProcs->IsX11Window([e window], 247 [e windowNumber])) 248 [self activateX:YES]; 249 } 250 } 251 252 /* We want to force sending to appkit if we're over the menu bar */ 253 if(!for_appkit) { 254 NSPoint NSlocation = [e locationInWindow]; 255 NSWindow *window = [e window]; 256 257 if (window != nil) { 258 NSRect frame = [window frame]; 259 NSlocation.x += frame.origin.x; 260 NSlocation.y += frame.origin.y; 261 } 262 263 NSRect NSframe = [[NSScreen mainScreen] frame]; 264 NSRect NSvisibleFrame = [[NSScreen mainScreen] visibleFrame]; 265 266 CGRect CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y, 267 NSframe.size.width, NSframe.size.height); 268 CGRect CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x, 269 NSvisibleFrame.origin.y, 270 NSvisibleFrame.size.width, 271 NSvisibleFrame.size.height); 272 CGPoint CGlocation = CGPointMake(NSlocation.x, NSlocation.y); 273 274 if(CGRectContainsPoint(CGframe, CGlocation) && 275 !CGRectContainsPoint(CGvisibleFrame, CGlocation)) 276 for_appkit = YES; 277 } 278 279 break; 280 281 case NSKeyDown: case NSKeyUp: 282 283 if(_x_active) { 284 static BOOL do_swallow = NO; 285 static int swallow_keycode; 286 287 if([e type] == NSKeyDown) { 288 /* Before that though, see if there are any global 289 * shortcuts bound to it. */ 290 291 if(darwinAppKitModMask & [e modifierFlags]) { 292 /* Override to force sending to Appkit */ 293 swallow_keycode = [e keyCode]; 294 do_swallow = YES; 295 for_x = NO; 296#if XPLUGIN_VERSION >= 1 297 } else if(X11EnableKeyEquivalents && 298 xp_is_symbolic_hotkey_event([e eventRef])) { 299 swallow_keycode = [e keyCode]; 300 do_swallow = YES; 301 for_x = NO; 302#endif 303 } else if(X11EnableKeyEquivalents && 304 [[self mainMenu] performKeyEquivalent:e]) { 305 swallow_keycode = [e keyCode]; 306 do_swallow = YES; 307 for_appkit = NO; 308 for_x = NO; 309 } else if(!quartzEnableRootless 310 && ([e modifierFlags] & ALL_KEY_MASKS) == (NSCommandKeyMask | NSAlternateKeyMask) 311 && ([e keyCode] == 0 /*a*/ || [e keyCode] == 53 /*Esc*/)) { 312 /* We have this here to force processing fullscreen 313 * toggle even if X11EnableKeyEquivalents is disabled */ 314 swallow_keycode = [e keyCode]; 315 do_swallow = YES; 316 for_x = NO; 317 for_appkit = NO; 318 DarwinSendDDXEvent(kXquartzToggleFullscreen, 0); 319 } else { 320 /* No kit window is focused, so send it to X. */ 321 for_appkit = NO; 322 } 323 } else { /* KeyUp */ 324 /* If we saw a key equivalent on the down, don't pass 325 * the up through to X. */ 326 if (do_swallow && [e keyCode] == swallow_keycode) { 327 do_swallow = NO; 328 for_x = NO; 329 } 330 } 331 } else { /* !_x_active */ 332 for_x = NO; 333 } 334 break; 335 336 case NSFlagsChanged: 337 /* Don't tell X11 about modifiers changing while it's not active */ 338 if (!_x_active) 339 for_x = NO; 340 break; 341 342 case NSAppKitDefined: 343 switch ([e subtype]) { 344 case NSApplicationActivatedEventType: 345 for_x = NO; 346 if ([self modalWindow] == nil) { 347 for_appkit = NO; 348 349 /* FIXME: hack to avoid having to pass the event to appkit, 350 which would cause it to raise one of its windows. */ 351 _appFlags._active = YES; 352 353 [self activateX:YES]; 354 355 /* Get the Spaces preference for SwitchOnActivate */ 356 (void)CFPreferencesAppSynchronize(CFSTR(".GlobalPreferences")); 357 BOOL switch_on_activate, ok; 358 switch_on_activate = CFPreferencesGetAppBooleanValue(CFSTR("AppleSpacesSwitchOnActivate"), CFSTR(".GlobalPreferences"), &ok); 359 if(!ok) 360 switch_on_activate = YES; 361 362 if ([e data2] & 0x10 && switch_on_activate) 363 DarwinSendDDXEvent(kXquartzBringAllToFront, 0); 364 } 365 break; 366 367 case 18: /* ApplicationDidReactivate */ 368 if (quartzHasRoot) for_appkit = NO; 369 break; 370 371 case NSApplicationDeactivatedEventType: 372 for_x = NO; 373 [self activateX:NO]; 374 break; 375 } 376 break; 377 378 default: break; /* for gcc */ 379 } 380 381 if (for_appkit) [super sendEvent:e]; 382 383 if (for_x) [self sendX11NSEvent:e]; 384} 385 386- (void) set_window_menu:(NSArray *)list { 387 [_controller set_window_menu:list]; 388} 389 390- (void) set_window_menu_check:(NSNumber *)n { 391 [_controller set_window_menu_check:n]; 392} 393 394- (void) set_apps_menu:(NSArray *)list { 395 [_controller set_apps_menu:list]; 396} 397 398- (void) set_front_process:unused { 399 [NSApp activateIgnoringOtherApps:YES]; 400 401 if ([self modalWindow] == nil) 402 [self activateX:YES]; 403} 404 405- (void) set_can_quit:(NSNumber *)state { 406 [_controller set_can_quit:[state boolValue]]; 407} 408 409- (void) server_ready:unused { 410 [_controller server_ready]; 411} 412 413- (void) show_hide_menubar:(NSNumber *)state { 414 /* Also shows/hides the dock */ 415 if ([state boolValue]) 416 SetSystemUIMode(kUIModeNormal, 0); 417 else 418 SetSystemUIMode(kUIModeAllHidden, quartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0); // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation" 419} 420 421 422/* user preferences */ 423 424/* Note that these functions only work for arrays whose elements 425 can be toll-free-bridged between NS and CF worlds. */ 426 427static const void *cfretain (CFAllocatorRef a, const void *b) { 428 return CFRetain (b); 429} 430 431static void cfrelease (CFAllocatorRef a, const void *b) { 432 CFRelease (b); 433} 434 435static CFMutableArrayRef nsarray_to_cfarray (NSArray *in) { 436 CFMutableArrayRef out; 437 CFArrayCallBacks cb; 438 NSObject *ns; 439 const CFTypeRef *cf; 440 int i, count; 441 442 memset (&cb, 0, sizeof (cb)); 443 cb.version = 0; 444 cb.retain = cfretain; 445 cb.release = cfrelease; 446 447 count = [in count]; 448 out = CFArrayCreateMutable (NULL, count, &cb); 449 450 for (i = 0; i < count; i++) { 451 ns = [in objectAtIndex:i]; 452 453 if ([ns isKindOfClass:[NSArray class]]) 454 cf = (CFTypeRef) nsarray_to_cfarray ((NSArray *) ns); 455 else 456 cf = CFRetain ((CFTypeRef) ns); 457 458 CFArrayAppendValue (out, cf); 459 CFRelease (cf); 460 } 461 462 return out; 463} 464 465static NSMutableArray * cfarray_to_nsarray (CFArrayRef in) { 466 NSMutableArray *out; 467 const CFTypeRef *cf; 468 NSObject *ns; 469 int i, count; 470 471 count = CFArrayGetCount (in); 472 out = [[NSMutableArray alloc] initWithCapacity:count]; 473 474 for (i = 0; i < count; i++) { 475 cf = CFArrayGetValueAtIndex (in, i); 476 477 if (CFGetTypeID (cf) == CFArrayGetTypeID ()) 478 ns = cfarray_to_nsarray ((CFArrayRef) cf); 479 else 480 ns = [(id)cf retain]; 481 482 [out addObject:ns]; 483 [ns release]; 484 } 485 486 return out; 487} 488 489- (CFPropertyListRef) prefs_get:(NSString *)key { 490 CFPropertyListRef value; 491 492 value = CFPreferencesCopyAppValue ((CFStringRef) key, app_prefs_domain_cfstr); 493 494 if (value == NULL) { 495 static CFDictionaryRef defaults; 496 497 if (defaults == NULL) { 498 CFStringRef error = NULL; 499 CFDataRef data; 500 CFURLRef url; 501 SInt32 error_code; 502 503 url = (CFURLCreateFromFileSystemRepresentation 504 (NULL, (unsigned char *)DEFAULTS_FILE, strlen (DEFAULTS_FILE), false)); 505 if (CFURLCreateDataAndPropertiesFromResource (NULL, url, &data, 506 NULL, NULL, &error_code)) { 507 defaults = (CFPropertyListCreateFromXMLData 508 (NULL, data, kCFPropertyListMutableContainersAndLeaves, &error)); 509 if (error != NULL) CFRelease (error); 510 CFRelease (data); 511 } 512 CFRelease (url); 513 514 if (defaults != NULL) { 515 NSMutableArray *apps, *elt; 516 int count, i; 517 NSString *name, *nname; 518 519 /* Localize the names in the default apps menu. */ 520 521 apps = [(NSDictionary *)defaults objectForKey:@PREFS_APPSMENU]; 522 if (apps != nil) { 523 count = [apps count]; 524 for (i = 0; i < count; i++) { 525 elt = [apps objectAtIndex:i]; 526 if (elt != nil && [elt isKindOfClass:[NSArray class]]) { 527 name = [elt objectAtIndex:0]; 528 if (name != nil) { 529 nname = NSLocalizedString (name, nil); 530 if (nname != nil && nname != name) 531 [elt replaceObjectAtIndex:0 withObject:nname]; 532 } 533 } 534 } 535 } 536 } 537 } 538 539 if (defaults != NULL) value = CFDictionaryGetValue (defaults, key); 540 if (value != NULL) CFRetain (value); 541 } 542 543 return value; 544} 545 546- (int) prefs_get_integer:(NSString *)key default:(int)def { 547 CFPropertyListRef value; 548 int ret; 549 550 value = [self prefs_get:key]; 551 552 if (value != NULL && CFGetTypeID (value) == CFNumberGetTypeID ()) 553 CFNumberGetValue (value, kCFNumberIntType, &ret); 554 else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) 555 ret = CFStringGetIntValue (value); 556 else 557 ret = def; 558 559 if (value != NULL) CFRelease (value); 560 561 return ret; 562} 563 564- (const char *) prefs_get_string:(NSString *)key default:(const char *)def { 565 CFPropertyListRef value; 566 const char *ret = NULL; 567 568 value = [self prefs_get:key]; 569 570 if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) { 571 NSString *s = (NSString *) value; 572 573 ret = [s UTF8String]; 574 } 575 576 if (value != NULL) CFRelease (value); 577 578 return ret != NULL ? ret : def; 579} 580 581- (float) prefs_get_float:(NSString *)key default:(float)def { 582 CFPropertyListRef value; 583 float ret = def; 584 585 value = [self prefs_get:key]; 586 587 if (value != NULL 588 && CFGetTypeID (value) == CFNumberGetTypeID () 589 && CFNumberIsFloatType (value)) 590 CFNumberGetValue (value, kCFNumberFloatType, &ret); 591 else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) 592 ret = CFStringGetDoubleValue (value); 593 594 if (value != NULL) CFRelease (value); 595 596 return ret; 597} 598 599- (int) prefs_get_boolean:(NSString *)key default:(int)def { 600 CFPropertyListRef value; 601 int ret = def; 602 603 value = [self prefs_get:key]; 604 605 if (value != NULL) { 606 if (CFGetTypeID (value) == CFNumberGetTypeID ()) 607 CFNumberGetValue (value, kCFNumberIntType, &ret); 608 else if (CFGetTypeID (value) == CFBooleanGetTypeID ()) 609 ret = CFBooleanGetValue (value); 610 else if (CFGetTypeID (value) == CFStringGetTypeID ()) { 611 const char *tem = [(NSString *) value UTF8String]; 612 if (strcasecmp (tem, "true") == 0 || strcasecmp (tem, "yes") == 0) 613 ret = YES; 614 else 615 ret = NO; 616 } 617 618 CFRelease (value); 619 } 620 return ret; 621} 622 623- (NSArray *) prefs_get_array:(NSString *)key { 624 NSArray *ret = nil; 625 CFPropertyListRef value; 626 627 value = [self prefs_get:key]; 628 629 if (value != NULL) { 630 if (CFGetTypeID (value) == CFArrayGetTypeID ()) 631 ret = [cfarray_to_nsarray (value) autorelease]; 632 633 CFRelease (value); 634 } 635 636 return ret; 637} 638 639- (void) prefs_set_integer:(NSString *)key value:(int)value { 640 CFNumberRef x; 641 642 x = CFNumberCreate (NULL, kCFNumberIntType, &value); 643 644 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, app_prefs_domain_cfstr, 645 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 646 647 CFRelease (x); 648} 649 650- (void) prefs_set_float:(NSString *)key value:(float)value { 651 CFNumberRef x; 652 653 x = CFNumberCreate (NULL, kCFNumberFloatType, &value); 654 655 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, app_prefs_domain_cfstr, 656 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 657 658 CFRelease (x); 659} 660 661- (void) prefs_set_boolean:(NSString *)key value:(int)value { 662 CFPreferencesSetValue ((CFStringRef) key, 663 (CFTypeRef) (value ? kCFBooleanTrue 664 : kCFBooleanFalse), app_prefs_domain_cfstr, 665 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 666 667} 668 669- (void) prefs_set_array:(NSString *)key value:(NSArray *)value { 670 CFArrayRef cfarray; 671 672 cfarray = nsarray_to_cfarray (value); 673 CFPreferencesSetValue ((CFStringRef) key, 674 (CFTypeRef) cfarray, 675 app_prefs_domain_cfstr, 676 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 677 CFRelease (cfarray); 678} 679 680- (void) prefs_set_string:(NSString *)key value:(NSString *)value { 681 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) value, 682 app_prefs_domain_cfstr, kCFPreferencesCurrentUser, 683 kCFPreferencesAnyHost); 684} 685 686- (void) prefs_synchronize { 687 CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication); 688} 689 690- (void) read_defaults 691{ 692 NSString *nsstr; 693 const char *tem; 694 695 quartzUseSysBeep = [self prefs_get_boolean:@PREFS_SYSBEEP 696 default:quartzUseSysBeep]; 697 quartzEnableRootless = [self prefs_get_boolean:@PREFS_ROOTLESS 698 default:quartzEnableRootless]; 699 quartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU 700 default:quartzFullscreenMenu]; 701 quartzFullscreenDisableHotkeys = ![self prefs_get_boolean: 702 @PREFS_FULLSCREEN_HOTKEYS default:!quartzFullscreenDisableHotkeys]; 703 darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS 704 default:darwinFakeButtons]; 705 if (darwinFakeButtons) { 706 const char *fake2, *fake3; 707 708 fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL]; 709 fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL]; 710 711 if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList(fake2, TRUE); 712 if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList(fake3, TRUE); 713 } 714 715 tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL]; 716 if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE); 717 718 tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL]; 719 if (tem != NULL) { 720 windowItemModMask = DarwinParseModifierList(tem, FALSE); 721 } else { 722 nsstr = NSLocalizedString (@"window item modifiers", @"window item modifiers"); 723 if(nsstr != NULL) { 724 tem = [nsstr UTF8String]; 725 if((tem != NULL) && strcmp(tem, "window item modifiers")) { 726 windowItemModMask = DarwinParseModifierList(tem, FALSE); 727 } 728 } 729 } 730 731 X11EnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS 732 default:X11EnableKeyEquivalents]; 733 734 darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP 735 default:darwinSyncKeymap]; 736 737 darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH 738 default:darwinDesiredDepth]; 739 740 noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS 741 default:FALSE]; 742} 743 744/* This will end up at the end of the responder chain. */ 745- (void) copy:sender { 746 DarwinSendDDXEvent(kXquartzPasteboardNotify, 1, 747 AppleWMCopyToPasteboard); 748} 749 750- (OSX_BOOL) x_active { 751 return _x_active; 752} 753 754@end 755 756static NSArray * 757array_with_strings_and_numbers (int nitems, const char **items, 758 const char *numbers) { 759 NSMutableArray *array, *subarray; 760 NSString *string, *number; 761 int i; 762 763 /* (Can't autorelease on the X server thread) */ 764 765 array = [[NSMutableArray alloc] initWithCapacity:nitems]; 766 767 for (i = 0; i < nitems; i++) { 768 subarray = [[NSMutableArray alloc] initWithCapacity:2]; 769 770 string = [[NSString alloc] initWithUTF8String:items[i]]; 771 [subarray addObject:string]; 772 [string release]; 773 774 if (numbers[i] != 0) { 775 number = [[NSString alloc] initWithFormat:@"%d", numbers[i]]; 776 [subarray addObject:number]; 777 [number release]; 778 } else 779 [subarray addObject:@""]; 780 781 [array addObject:subarray]; 782 [subarray release]; 783 } 784 785 return array; 786} 787 788void X11ApplicationSetWindowMenu (int nitems, const char **items, 789 const char *shortcuts) { 790 NSArray *array; 791 array = array_with_strings_and_numbers (nitems, items, shortcuts); 792 793 /* Send the array of strings over to the appkit thread */ 794 795 message_kit_thread (@selector (set_window_menu:), array); 796 [array release]; 797} 798 799void X11ApplicationSetWindowMenuCheck (int idx) { 800 NSNumber *n; 801 802 n = [[NSNumber alloc] initWithInt:idx]; 803 804 message_kit_thread (@selector (set_window_menu_check:), n); 805 806 [n release]; 807} 808 809void X11ApplicationSetFrontProcess (void) { 810 message_kit_thread (@selector (set_front_process:), nil); 811} 812 813void X11ApplicationSetCanQuit (int state) { 814 NSNumber *n; 815 816 n = [[NSNumber alloc] initWithBool:state]; 817 818 message_kit_thread (@selector (set_can_quit:), n); 819 820 [n release]; 821} 822 823void X11ApplicationServerReady (void) { 824 message_kit_thread (@selector (server_ready:), nil); 825} 826 827void X11ApplicationShowHideMenubar (int state) { 828 NSNumber *n; 829 830 n = [[NSNumber alloc] initWithBool:state]; 831 832 message_kit_thread (@selector (show_hide_menubar:), n); 833 834 [n release]; 835} 836 837static void check_xinitrc (void) { 838 char *tem, buf[1024]; 839 NSString *msg; 840 841 if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO]) 842 return; 843 844 tem = getenv ("HOME"); 845 if (tem == NULL) goto done; 846 847 snprintf (buf, sizeof (buf), "%s/.xinitrc", tem); 848 if (access (buf, F_OK) != 0) 849 goto done; 850 851 msg = NSLocalizedString (@"You have an existing ~/.xinitrc file.\n\n\ 852Windows displayed by X11 applications may not have titlebars, or may look \ 853different to windows displayed by native applications.\n\n\ 854Would you like to move aside the existing file and use the standard X11 \ 855environment the next time you start X11?", @"Startup xinitrc dialog"); 856 857 if(NSAlertDefaultReturn == NSRunAlertPanel (nil, msg, NSLocalizedString (@"Yes", @""), 858 NSLocalizedString (@"No", @""), nil)) { 859 char buf2[1024]; 860 int i = -1; 861 862 snprintf (buf2, sizeof (buf2), "%s.old", buf); 863 864 for(i = 1; access (buf2, F_OK) == 0; i++) 865 snprintf (buf2, sizeof (buf2), "%s.old.%d", buf, i); 866 867 rename (buf, buf2); 868 } 869 870 done: 871 [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES]; 872 [X11App prefs_synchronize]; 873} 874 875void X11ApplicationMain (int argc, char **argv, char **envp) { 876 NSAutoreleasePool *pool; 877 int *p; 878 879#ifdef DEBUG 880 while (access ("/tmp/x11-block", F_OK) == 0) sleep (1); 881#endif 882 883 pool = [[NSAutoreleasePool alloc] init]; 884 X11App = (X11Application *) [X11Application sharedApplication]; 885 init_ports (); 886 887 app_prefs_domain_cfstr = (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]; 888 889 [NSApp read_defaults]; 890 [NSBundle loadNibNamed:@"main" owner:NSApp]; 891 [[NSNotificationCenter defaultCenter] addObserver:NSApp 892 selector:@selector (became_key:) 893 name:NSWindowDidBecomeKeyNotification object:nil]; 894 895 /* 896 * The xpr Quartz mode is statically linked into this server. 897 * Initialize all the Quartz functions. 898 */ 899 QuartzModeBundleInit(); 900 901 /* Calculate the height of the menubar so we can avoid it. */ 902 aquaMenuBarHeight = NSHeight([[NSScreen mainScreen] frame]) - 903 NSMaxY([[NSScreen mainScreen] visibleFrame]); 904 905 /* Set the key layout seed before we start the server */ 906#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 907 last_key_layout = TISCopyCurrentKeyboardLayoutInputSource(); 908 909 if(!last_key_layout) 910 fprintf(stderr, "X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n"); 911#else 912 KLGetCurrentKeyboardLayout(&last_key_layout); 913 if(!last_key_layout) 914 fprintf(stderr, "X11ApplicationMain: Unable to determine KLGetCurrentKeyboardLayout() at startup.\n"); 915#endif 916 917 memset(keyInfo.keyMap, 0, sizeof(keyInfo.keyMap)); 918 if (!QuartzReadSystemKeymap(&keyInfo)) { 919 fprintf(stderr, "X11ApplicationMain: Could not build a valid keymap.\n"); 920 } 921 922 for(p=darwin_modifier_mask_list, modifierFlagsMask=0; *p; p++) { 923 modifierFlagsMask |= *p; 924 } 925 926 /* Tell the server thread that it can proceed */ 927 QuartzInitServer(argc, argv, envp); 928 929 /* This must be done after QuartzInitServer because it can result in 930 * an mieqEnqueue() - <rdar://problem/6300249> 931 */ 932 check_xinitrc(); 933 934 if(!xpbproxy_init()) 935 fprintf(stderr, "Error initializing xpbproxy\n"); 936 937 [NSApp run]; 938 /* not reached */ 939} 940 941@implementation X11Application (Private) 942 943#ifdef NX_DEVICELCMDKEYMASK 944/* This is to workaround a bug in the VNC server where we sometimes see the L 945 * modifier and sometimes see no "side" 946 */ 947static inline int ensure_flag(int flags, int device_independent, int device_dependents, int device_dependent_default) { 948 if( (flags & device_independent) && 949 !(flags & device_dependents)) 950 flags |= device_dependent_default; 951 return flags; 952} 953#endif 954 955- (void) sendX11NSEvent:(NSEvent *)e { 956 NSRect screen; 957 NSPoint location; 958 NSWindow *window; 959 int ev_button, ev_type; 960 float pointer_x, pointer_y, pressure, tilt_x, tilt_y; 961 DeviceIntPtr pDev; 962 int modifierFlags; 963 964 /* convert location to be relative to top-left of primary display */ 965 location = [e locationInWindow]; 966 window = [e window]; 967 screen = [[[NSScreen screens] objectAtIndex:0] frame]; 968 969 if (window != nil) { 970 NSRect frame = [window frame]; 971 pointer_x = location.x + frame.origin.x; 972 pointer_y = (screen.origin.y + screen.size.height) 973 - (location.y + frame.origin.y); 974 } else { 975 pointer_x = location.x; 976 pointer_y = (screen.origin.y + screen.size.height) - location.y; 977 } 978 979 /* Setup our valuators. These will range from 0 to 1 */ 980 pressure = 0; 981 tilt_x = 0; 982 tilt_y = 0; 983 984 modifierFlags = [e modifierFlags]; 985 986#ifdef NX_DEVICELCMDKEYMASK 987 /* This is to workaround a bug in the VNC server where we sometimes see the L 988 * modifier and sometimes see no "side" 989 */ 990 modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK, NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK, NX_DEVICELCTLKEYMASK); 991 modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK, NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK, NX_DEVICELSHIFTKEYMASK); 992 modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK, NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK, NX_DEVICELCMDKEYMASK); 993 modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK, NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK, NX_DEVICELALTKEYMASK); 994#endif 995 996 modifierFlags &= modifierFlagsMask; 997 998 /* We don't receive modifier key events while out of focus, and 3button 999 * emulation mucks this up, so we need to check our modifier flag state 1000 * on every event... ugg 1001 */ 1002 1003 if(darwin_modifier_flags != modifierFlags) 1004 DarwinUpdateModKeys(modifierFlags); 1005 1006 switch ([e type]) { 1007 case NSLeftMouseDown: ev_button=1; ev_type=ButtonPress; goto handle_mouse; 1008 case NSOtherMouseDown: ev_button=2; ev_type=ButtonPress; goto handle_mouse; 1009 case NSRightMouseDown: ev_button=3; ev_type=ButtonPress; goto handle_mouse; 1010 case NSLeftMouseUp: ev_button=1; ev_type=ButtonRelease; goto handle_mouse; 1011 case NSOtherMouseUp: ev_button=2; ev_type=ButtonRelease; goto handle_mouse; 1012 case NSRightMouseUp: ev_button=3; ev_type=ButtonRelease; goto handle_mouse; 1013 case NSLeftMouseDragged: ev_button=1; ev_type=MotionNotify; goto handle_mouse; 1014 case NSOtherMouseDragged: ev_button=2; ev_type=MotionNotify; goto handle_mouse; 1015 case NSRightMouseDragged: ev_button=3; ev_type=MotionNotify; goto handle_mouse; 1016 case NSMouseMoved: ev_button=0; ev_type=MotionNotify; goto handle_mouse; 1017 case NSTabletPoint: ev_button=0; ev_type=MotionNotify; goto handle_mouse; 1018 1019 handle_mouse: 1020 pDev = darwinPointer; 1021 1022 /* NSTabletPoint can have no subtype */ 1023 if([e type] != NSTabletPoint && 1024 [e subtype] == NSTabletProximityEventSubtype) { 1025 switch([e pointingDeviceType]) { 1026 case NSEraserPointingDevice: 1027 darwinTabletCurrent=darwinTabletEraser; 1028 break; 1029 case NSPenPointingDevice: 1030 darwinTabletCurrent=darwinTabletStylus; 1031 break; 1032 case NSCursorPointingDevice: 1033 case NSUnknownPointingDevice: 1034 default: 1035 darwinTabletCurrent=darwinTabletCursor; 1036 break; 1037 } 1038 1039 /* NSTabletProximityEventSubtype doesn't encode pressure ant tilt 1040 * So we just pretend the motion was caused by the mouse. Hopefully 1041 * we'll have a better solution for this in the future (like maybe 1042 * NSTabletProximityEventSubtype will come from NSTabletPoint 1043 * rather than NSMouseMoved. 1044 pressure = [e pressure]; 1045 tilt_x = [e tilt].x; 1046 tilt_y = [e tilt].y; 1047 pDev = darwinTabletCurrent; 1048 */ 1049 1050 DarwinSendProximityEvents([e isEnteringProximity]?ProximityIn:ProximityOut, 1051 pointer_x, pointer_y); 1052 } 1053 1054 if ([e type] == NSTabletPoint || [e subtype] == NSTabletPointEventSubtype) { 1055 pressure = [e pressure]; 1056 tilt_x = [e tilt].x; 1057 tilt_y = [e tilt].y; 1058 1059 pDev = darwinTabletCurrent; 1060 } 1061 1062/* Older libXplugin (Tiger/"Stock" Leopard) aren't thread safe, so we can't call xp_find_window from the Appkit thread */ 1063#ifdef XPLUGIN_VERSION 1064#if XPLUGIN_VERSION > 0 1065 if(!quartzServerVisible) { 1066 xp_window_id wid; 1067 1068 /* Sigh. Need to check that we're really over one of 1069 * our windows. (We need to receive pointer events while 1070 * not in the foreground, but we don't want to receive them 1071 * when another window is over us or we might show a tooltip) 1072 */ 1073 1074 wid = 0; 1075 1076 if (xp_find_window(pointer_x, pointer_y, 0, &wid) == XP_Success && 1077 wid == 0) 1078 return; 1079 } 1080#endif 1081#endif 1082 1083 DarwinSendPointerEvents(pDev, ev_type, ev_button, pointer_x, pointer_y, 1084 pressure, tilt_x, tilt_y); 1085 1086 break; 1087 1088 case NSTabletProximity: 1089 switch([e pointingDeviceType]) { 1090 case NSEraserPointingDevice: 1091 darwinTabletCurrent=darwinTabletEraser; 1092 break; 1093 case NSPenPointingDevice: 1094 darwinTabletCurrent=darwinTabletStylus; 1095 break; 1096 case NSCursorPointingDevice: 1097 case NSUnknownPointingDevice: 1098 default: 1099 darwinTabletCurrent=darwinTabletCursor; 1100 break; 1101 } 1102 1103 DarwinSendProximityEvents([e isEnteringProximity]?ProximityIn:ProximityOut, 1104 pointer_x, pointer_y); 1105 break; 1106 1107 case NSScrollWheel: 1108 DarwinSendScrollEvents([e deltaX], [e deltaY], pointer_x, pointer_y, 1109 pressure, tilt_x, tilt_y); 1110 break; 1111 1112 case NSKeyDown: case NSKeyUp: 1113 if(darwinSyncKeymap) { 1114#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 1115 TISInputSourceRef key_layout = TISCopyCurrentKeyboardLayoutInputSource(); 1116 TISInputSourceRef clear; 1117 if (CFEqual(key_layout, last_key_layout)) { 1118 CFRelease(key_layout); 1119 } else { 1120 /* Swap/free thread-safely */ 1121 clear = last_key_layout; 1122 last_key_layout = key_layout; 1123 CFRelease(clear); 1124#else 1125 KeyboardLayoutRef key_layout; 1126 KLGetCurrentKeyboardLayout(&key_layout); 1127 if(key_layout != last_key_layout) { 1128 last_key_layout = key_layout; 1129#endif 1130 1131 /* Update keyInfo */ 1132 pthread_mutex_lock(&keyInfo_mutex); 1133 memset(keyInfo.keyMap, 0, sizeof(keyInfo.keyMap)); 1134 if (!QuartzReadSystemKeymap(&keyInfo)) { 1135 fprintf(stderr, "sendX11NSEvent: Could not build a valid keymap.\n"); 1136 } 1137 pthread_mutex_unlock(&keyInfo_mutex); 1138 1139 /* Tell server thread to deal with new keyInfo */ 1140 DarwinSendDDXEvent(kXquartzReloadKeymap, 0); 1141 } 1142 } 1143 1144 /* Avoid stuck keys on context switch */ 1145 if(keyState[[e keyCode]] == [e type]) 1146 return; 1147 keyState[[e keyCode]] = [e type]; 1148 1149 DarwinSendKeyboardEvents(([e type] == NSKeyDown) ? KeyPress : KeyRelease, [e keyCode]); 1150 break; 1151 1152 default: break; /* for gcc */ 1153 } 1154} 1155@end 1156