1/* X11Application.m -- subclass of NSApplication to multiplex events 2 $Id: X11Application.m,v 1.53 2003/09/13 02:00:46 jharper Exp $ 3 4 Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 5 6 Permission is hereby granted, free of charge, to any person 7 obtaining a copy of this software and associated documentation files 8 (the "Software"), to deal in the Software without restriction, 9 including without limitation the rights to use, copy, modify, merge, 10 publish, distribute, sublicense, and/or sell copies of the Software, 11 and to permit persons to whom the Software is furnished to do so, 12 subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be 15 included in all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 21 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 DEALINGS IN THE SOFTWARE. 25 26 Except as contained in this notice, the name(s) of the above 27 copyright holders shall not be used in advertising or otherwise to 28 promote the sale, use or other dealings in this Software without 29 prior written authorization. */ 30 31#import "X11Application.h" 32#include <Carbon/Carbon.h> 33 34/* ouch! */ 35#define BOOL X_BOOL 36# include "Xproto.h" 37#define WindowPtr X_WindowPtr 38#define Cursor X_Cursor 39# include "quartz.h" 40# define _APPLEWM_SERVER_ 41# include "applewm.h" 42# include "X.h" 43#undef Cursor 44#undef WindowPtr 45#undef BOOL 46 47#include "xf86Version.h" 48 49#include <mach/mach.h> 50#include <unistd.h> 51#include <pthread.h> 52 53#define DEFAULTS_FILE "/etc/X11/xserver/Xquartz.plist" 54 55int X11EnableKeyEquivalents = TRUE; 56 57X11Application *X11App; 58 59#define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask \ 60 | NSAlternateKeyMask | NSCommandKeyMask) 61 62@implementation X11Application 63 64typedef struct message_struct message; 65struct message_struct { 66 mach_msg_header_t hdr; 67 SEL selector; 68 NSObject *arg; 69}; 70 71static mach_port_t _port; 72 73static void send_nsevent (NSEventType type, NSEvent *e); 74 75/* avoid header conflict hell */ 76extern int RootlessKnowsWindowNumber (int number); 77extern void DarwinEnqueueEvent (const xEvent *e); 78 79static void 80init_ports (void) 81{ 82 kern_return_t r; 83 NSPort *p; 84 85 if (_port != MACH_PORT_NULL) 86 return; 87 88 r = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &_port); 89 if (r != KERN_SUCCESS) 90 return; 91 92 p = [NSMachPort portWithMachPort:_port]; 93 [p setDelegate:NSApp]; 94 [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 95} 96 97static void 98message_kit_thread (SEL selector, NSObject *arg) 99{ 100 message msg; 101 kern_return_t r; 102 103 msg.hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0); 104 msg.hdr.msgh_size = sizeof (msg); 105 msg.hdr.msgh_remote_port = _port; 106 msg.hdr.msgh_local_port = MACH_PORT_NULL; 107 msg.hdr.msgh_reserved = 0; 108 msg.hdr.msgh_id = 0; 109 110 msg.selector = selector; 111 msg.arg = [arg retain]; 112 113 r = mach_msg (&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size, 114 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 115 if (r != KERN_SUCCESS) 116 fprintf (stderr, "%s: mach_msg failed: %x\n", __FUNCTION__, r); 117} 118 119- (void) handleMachMessage:(void *)_msg 120{ 121 message *msg = _msg; 122 123 [self performSelector:msg->selector withObject:msg->arg]; 124 [msg->arg release]; 125} 126 127- (void) set_controller:obj 128{ 129 if (_controller == nil) 130 _controller = [obj retain]; 131} 132 133- (void) dealloc 134{ 135 if (_controller != nil) 136 [_controller release]; 137 138 if (_port != MACH_PORT_NULL) 139 mach_port_deallocate (mach_task_self (), _port); 140 141 [super dealloc]; 142} 143 144- (void) orderFrontStandardAboutPanel: (id) sender 145{ 146 NSMutableDictionary *dict; 147 NSDictionary *infoDict; 148 NSString *tem; 149 150 dict = [NSMutableDictionary dictionaryWithCapacity:2]; 151 infoDict = [[NSBundle mainBundle] infoDictionary]; 152 153 [dict setObject: NSLocalizedString (@"The X Window System", @"About panel") 154 forKey:@"ApplicationName"]; 155 156 tem = [infoDict objectForKey:@"CFBundleShortVersionString"]; 157 158 [dict setObject:[NSString stringWithFormat:@"X11 %@ - XFree86 %d.%d.%d", 159 tem, XF86_VERSION_MAJOR, XF86_VERSION_MINOR, 160 XF86_VERSION_PATCH] forKey:@"ApplicationVersion"]; 161 162 [self orderFrontStandardAboutPanelWithOptions: dict]; 163} 164 165- (void) activateX:(BOOL)state 166{ 167 /* Create a TSM document that supports full Unicode input, and 168 have it activated while X is active (unless using the old 169 keymapping files) */ 170 static TSMDocumentID x11_document; 171 172 if (state) 173 { 174 QuartzMessageMainThread (kXquartzActivate, 0); 175 176 if (!_x_active) 177 { 178 if (x11_document == 0 && darwinKeymapFile == NULL) 179 { 180 OSType types[1]; 181 types[0] = kUnicodeDocument; 182 NewTSMDocument (1, types, &x11_document, 0); 183 } 184 185 if (x11_document != 0) 186 ActivateTSMDocument (x11_document); 187 } 188 } 189 else 190 { 191 QuartzMessageMainThread (kXquartzDeactivate, 0); 192 193 if (_x_active) 194 { 195 if (x11_document != 0) 196 DeactivateTSMDocument (x11_document); 197 } 198 } 199 200 _x_active = state; 201} 202 203- (void) became_key:(NSWindow *)win 204{ 205 [self activateX:NO]; 206} 207 208- (void) sendEvent:(NSEvent *)e 209{ 210 NSEventType type; 211 BOOL for_appkit, for_x; 212 213 type = [e type]; 214 215 /* By default pass down the responder chain and to X. */ 216 for_appkit = YES; 217 for_x = YES; 218 219 switch (type) 220 { 221 case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: 222 case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: 223 if ([e window] != nil) 224 { 225 /* Pointer event has a window. Probably something for the kit. */ 226 227 for_x = NO; 228 229 if (_x_active) 230 [self activateX:NO]; 231 } 232 else if ([self modalWindow] == nil) 233 { 234 /* Must be an X window. Tell appkit it doesn't have focus. */ 235 236 for_appkit = NO; 237 238 if ([self isActive]) 239 { 240 [self deactivate]; 241 242 if (!_x_active && RootlessKnowsWindowNumber ([e windowNumber])) 243 [self activateX:YES]; 244 } 245 } 246 break; 247 248 case NSKeyDown: case NSKeyUp: 249 if (_x_active) 250 { 251 static int swallow_up; 252 253 /* No kit window is focused, so send it to X. */ 254 255 for_appkit = NO; 256 257 if (type == NSKeyDown) 258 { 259 /* Before that though, see if there are any global 260 shortcuts bound to it. */ 261 262 if (X11EnableKeyEquivalents 263 && [[self mainMenu] performKeyEquivalent:e]) 264 { 265 swallow_up = [e keyCode]; 266 for_x = NO; 267 } 268 else if (!quartzEnableRootless 269 && ([e modifierFlags] & ALL_KEY_MASKS) 270 == (NSCommandKeyMask | NSAlternateKeyMask) 271 && ([e keyCode] == 0 /*a*/ 272 || [e keyCode] == 53 /*Esc*/)) 273 { 274 swallow_up = 0; 275 for_x = NO; 276 QuartzMessageMainThread (kXquartzToggleFullscreen, 0); 277 } 278 } 279 else 280 { 281 /* If we saw a key equivalent on the down, don't pass 282 the up through to X. */ 283 284 if (swallow_up != 0 && [e keyCode] == swallow_up) 285 { 286 swallow_up = 0; 287 for_x = NO; 288 } 289 } 290 } 291 else 292 { 293 for_x = NO; 294 } 295 break; 296 297 case NSFlagsChanged: 298 /* For the l33t X users who remap modifier keys to normal keysyms. */ 299 if (!_x_active) 300 for_x = NO; 301 break; 302 303 case NSAppKitDefined: 304 switch ([e subtype]) 305 { 306 case NSApplicationActivatedEventType: 307 for_x = NO; 308 if ([self modalWindow] == nil) 309 { 310 for_appkit = NO; 311 312 /* FIXME: hack to avoid having to pass the event to appkit, 313 which would cause it to raise one of its windows. */ 314 _appFlags._active = YES; 315 316 [self activateX:YES]; 317 } 318 break; 319 320 case 18: /* ApplicationDidReactivate */ 321 if (quartzHasRoot) 322 for_appkit = NO; 323 break; 324 325 case NSApplicationDeactivatedEventType: 326 for_x = NO; 327 [self activateX:NO]; 328 break; 329 } 330 break; 331 332 default: break; /* for gcc */ 333 } 334 335 if (for_appkit) 336 { 337 [super sendEvent:e]; 338 } 339 340 if (for_x) 341 { 342 send_nsevent (type, e); 343 } 344} 345 346- (void) set_window_menu:(NSArray *)list 347{ 348 [_controller set_window_menu:list]; 349} 350 351- (void) set_window_menu_check:(NSNumber *)n 352{ 353 [_controller set_window_menu_check:n]; 354} 355 356- (void) set_apps_menu:(NSArray *)list 357{ 358 [_controller set_apps_menu:list]; 359} 360 361- (void) set_front_process:unused 362{ 363 [NSApp activateIgnoringOtherApps:YES]; 364 365 if ([self modalWindow] == nil) 366 [self activateX:YES]; 367} 368 369- (void) set_can_quit:(NSNumber *)state 370{ 371 [_controller set_can_quit:[state boolValue]]; 372} 373 374- (void) server_ready:unused 375{ 376 [_controller server_ready]; 377} 378 379- (void) show_hide_menubar:(NSNumber *)state 380{ 381 if ([state boolValue]) 382 ShowMenuBar (); 383 else 384 HideMenuBar (); 385} 386 387 388/* user preferences */ 389 390/* Note that these functions only work for arrays whose elements 391 can be toll-free-bridged between NS and CF worlds. */ 392 393static const void *cfretain (CFAllocatorRef a, const void *b) { 394 return CFRetain (b); 395} 396static void cfrelease (CFAllocatorRef a, const void *b) { 397 CFRelease (b); 398} 399static CFMutableArrayRef 400nsarray_to_cfarray (NSArray *in) 401{ 402 CFMutableArrayRef out; 403 CFArrayCallBacks cb; 404 NSObject *ns; 405 const CFTypeRef *cf; 406 int i, count; 407 408 memset (&cb, 0, sizeof (cb)); 409 cb.version = 0; 410 cb.retain = cfretain; 411 cb.release = cfrelease; 412 413 count = [in count]; 414 out = CFArrayCreateMutable (NULL, count, &cb); 415 416 for (i = 0; i < count; i++) 417 { 418 ns = [in objectAtIndex:i]; 419 420 if ([ns isKindOfClass:[NSArray class]]) 421 cf = (CFTypeRef) nsarray_to_cfarray ((NSArray *) ns); 422 else 423 cf = CFRetain ((CFTypeRef) ns); 424 425 CFArrayAppendValue (out, cf); 426 CFRelease (cf); 427 } 428 429 return out; 430} 431static NSMutableArray * 432cfarray_to_nsarray (CFArrayRef in) 433{ 434 NSMutableArray *out; 435 const CFTypeRef *cf; 436 NSObject *ns; 437 int i, count; 438 439 count = CFArrayGetCount (in); 440 out = [[NSMutableArray alloc] initWithCapacity:count]; 441 442 for (i = 0; i < count; i++) 443 { 444 cf = CFArrayGetValueAtIndex (in, i); 445 446 if (CFGetTypeID (cf) == CFArrayGetTypeID ()) 447 ns = cfarray_to_nsarray ((CFArrayRef) cf); 448 else 449 ns = [(id)cf retain]; 450 451 [out addObject:ns]; 452 [ns release]; 453 } 454 455 return out; 456} 457 458- (CFPropertyListRef) prefs_get:(NSString *)key 459{ 460 CFPropertyListRef value; 461 462 value = CFPreferencesCopyAppValue ((CFStringRef) key, CFSTR (APP_PREFS)); 463 464 if (value == NULL) 465 { 466 static CFDictionaryRef defaults; 467 468 if (defaults == NULL) 469 { 470 CFStringRef error = NULL; 471 CFDataRef data; 472 CFURLRef url; 473 SInt32 error_code; 474 475 url = (CFURLCreateFromFileSystemRepresentation 476 (NULL, DEFAULTS_FILE, strlen (DEFAULTS_FILE), false)); 477 if (CFURLCreateDataAndPropertiesFromResource (NULL, url, &data, 478 NULL, NULL, 479 &error_code)) 480 { 481 defaults = (CFPropertyListCreateFromXMLData 482 (NULL, data, kCFPropertyListImmutable, &error)); 483 if (error != NULL) 484 CFRelease (error); 485 CFRelease (data); 486 } 487 CFRelease (url); 488 } 489 490 if (defaults != NULL) 491 value = CFDictionaryGetValue (defaults, key); 492 493 if (value != NULL) 494 CFRetain (value); 495 } 496 497 return value; 498} 499 500- (int) prefs_get_integer:(NSString *)key default:(int)def 501{ 502 CFPropertyListRef value; 503 int ret; 504 505 value = [self prefs_get:key]; 506 507 if (value != NULL && CFGetTypeID (value) == CFNumberGetTypeID ()) 508 CFNumberGetValue (value, kCFNumberIntType, &ret); 509 else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) 510 ret = CFStringGetIntValue (value); 511 else 512 ret = def; 513 514 if (value != NULL) 515 CFRelease (value); 516 517 return ret; 518} 519 520- (const char *) prefs_get_string:(NSString *)key default:(const char *)def 521{ 522 CFPropertyListRef value; 523 const char *ret = NULL; 524 525 value = [self prefs_get:key]; 526 527 if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) 528 { 529 NSString *s = (NSString *) value; 530 531 ret = [s UTF8String]; 532 } 533 534 if (value != NULL) 535 CFRelease (value); 536 537 return ret != NULL ? ret : def; 538} 539 540- (float) prefs_get_float:(NSString *)key default:(float)def 541{ 542 CFPropertyListRef value; 543 float ret = def; 544 545 value = [self prefs_get:key]; 546 547 if (value != NULL 548 && CFGetTypeID (value) == CFNumberGetTypeID () 549 && CFNumberIsFloatType (value)) 550 { 551 CFNumberGetValue (value, kCFNumberFloatType, &ret); 552 } 553 else if (value != NULL && CFGetTypeID (value) == CFStringGetTypeID ()) 554 { 555 ret = CFStringGetDoubleValue (value); 556 } 557 558 if (value != NULL) 559 CFRelease (value); 560 561 return ret; 562} 563 564- (int) prefs_get_boolean:(NSString *)key default:(int)def 565{ 566 CFPropertyListRef value; 567 int ret = def; 568 569 value = [self prefs_get:key]; 570 571 if (value != NULL) 572 { 573 if (CFGetTypeID (value) == CFNumberGetTypeID ()) 574 CFNumberGetValue (value, kCFNumberIntType, &ret); 575 else if (CFGetTypeID (value) == CFBooleanGetTypeID ()) 576 ret = CFBooleanGetValue (value); 577 else if (CFGetTypeID (value) == CFStringGetTypeID ()) 578 { 579 const char *tem = [(NSString *) value lossyCString]; 580 if (strcasecmp (tem, "true") == 0 || strcasecmp (tem, "yes") == 0) 581 ret = YES; 582 else 583 ret = NO; 584 } 585 586 CFRelease (value); 587 } 588 589 return ret; 590} 591 592- (NSArray *) prefs_get_array:(NSString *)key 593{ 594 NSArray *ret = nil; 595 CFPropertyListRef value; 596 597 value = [self prefs_get:key]; 598 599 if (value != NULL) 600 { 601 if (CFGetTypeID (value) == CFArrayGetTypeID ()) 602 ret = [cfarray_to_nsarray (value) autorelease]; 603 604 CFRelease (value); 605 } 606 607 return ret; 608} 609 610- (void) prefs_set_integer:(NSString *)key value:(int)value 611{ 612 CFNumberRef x; 613 614 x = CFNumberCreate (NULL, kCFNumberIntType, &value); 615 616 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, CFSTR (APP_PREFS), 617 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 618 619 CFRelease (x); 620} 621 622- (void) prefs_set_float:(NSString *)key value:(float)value 623{ 624 CFNumberRef x; 625 626 x = CFNumberCreate (NULL, kCFNumberFloatType, &value); 627 628 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) x, CFSTR (APP_PREFS), 629 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 630 631 CFRelease (x); 632} 633 634- (void) prefs_set_boolean:(NSString *)key value:(int)value 635{ 636 CFPreferencesSetValue ((CFStringRef) key, 637 (CFTypeRef) value ? kCFBooleanTrue 638 : kCFBooleanFalse, CFSTR (APP_PREFS), 639 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 640 641} 642 643- (void) prefs_set_array:(NSString *)key value:(NSArray *)value 644{ 645 CFArrayRef cfarray; 646 647 cfarray = nsarray_to_cfarray (value); 648 CFPreferencesSetValue ((CFStringRef) key, 649 (CFTypeRef) cfarray, 650 CFSTR (APP_PREFS), 651 kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 652 CFRelease (cfarray); 653} 654 655- (void) prefs_set_string:(NSString *)key value:(NSString *)value 656{ 657 CFPreferencesSetValue ((CFStringRef) key, (CFTypeRef) value, 658 CFSTR (APP_PREFS), kCFPreferencesCurrentUser, 659 kCFPreferencesAnyHost); 660} 661 662- (void) prefs_synchronize 663{ 664 CFPreferencesAppSynchronize (kCFPreferencesCurrentApplication); 665} 666 667- (void) read_defaults 668{ 669 extern int darwinFakeButtons; 670 const char *tem; 671 672 quartzUseSysBeep = [self prefs_get_boolean:@PREFS_SYSBEEP 673 default:quartzUseSysBeep]; 674 quartzEnableRootless = [self prefs_get_boolean:@PREFS_ROOTLESS 675 default:quartzEnableRootless]; 676 quartzFullscreenDisableHotkeys = ![self prefs_get_boolean: 677 @PREFS_FULLSCREEN_HOTKEYS default: 678 !quartzFullscreenDisableHotkeys]; 679 quartzXpluginOptions = [self prefs_get_integer:@PREFS_XP_OPTIONS 680 default:quartzXpluginOptions]; 681 682 darwinSwapAltMeta = [self prefs_get_boolean:@PREFS_SWAP_ALT_META 683 default:darwinSwapAltMeta]; 684 darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS 685 default:darwinFakeButtons]; 686 if (darwinFakeButtons) 687 { 688 const char *fake2, *fake3; 689 690 fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL]; 691 fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL]; 692 693 DarwinSetFakeButtons (fake2, fake3); 694 } 695 696 X11EnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS 697 default:X11EnableKeyEquivalents]; 698 699 darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP 700 default:darwinSyncKeymap]; 701 702 tem = [self prefs_get_string:@PREFS_KEYMAP_FILE default:NULL]; 703 if (tem != NULL) 704 darwinKeymapFile = strdup (tem); 705 706 quartzDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH 707 default:quartzDesiredDepth]; 708} 709 710/* This will end up at the end of the responder chain. */ 711- (void) copy:sender 712{ 713 QuartzMessageMainThread (kXquartzPasteboardNotify, 1, 714 AppleWMCopyToPasteboard); 715} 716 717- (BOOL) x_active 718{ 719 return _x_active; 720} 721 722@end 723 724static NSArray * 725array_with_strings_and_numbers (int nitems, const char **items, 726 const char *numbers) 727{ 728 NSMutableArray *array, *subarray; 729 NSString *string; 730 NSString *number; 731 int i; 732 733 /* (Can't autorelease on the X server thread) */ 734 735 array = [[NSMutableArray alloc] initWithCapacity:nitems]; 736 737 for (i = 0; i < nitems; i++) 738 { 739 subarray = [[NSMutableArray alloc] initWithCapacity:2]; 740 741 string = [[NSString alloc] initWithUTF8String:items[i]]; 742 [subarray addObject:string]; 743 [string release]; 744 745 if (numbers[i] != 0) 746 { 747 number = [[NSString alloc] initWithFormat:@"%d", numbers[i]]; 748 [subarray addObject:number]; 749 [number release]; 750 } 751 else 752 [subarray addObject:@""]; 753 754 [array addObject:subarray]; 755 [subarray release]; 756 } 757 758 return array; 759} 760 761void 762X11ApplicationSetWindowMenu (int nitems, const char **items, 763 const char *shortcuts) 764{ 765 NSArray *array; 766 767 array = array_with_strings_and_numbers (nitems, items, shortcuts); 768 769 /* Send the array of strings over to the appkit thread */ 770 771 message_kit_thread (@selector (set_window_menu:), array); 772 [array release]; 773} 774 775void 776X11ApplicationSetWindowMenuCheck (int idx) 777{ 778 NSNumber *n; 779 780 n = [[NSNumber alloc] initWithInt:idx]; 781 782 message_kit_thread (@selector (set_window_menu_check:), n); 783 784 [n release]; 785} 786 787void 788X11ApplicationSetFrontProcess (void) 789{ 790 message_kit_thread (@selector (set_front_process:), nil); 791} 792 793void 794X11ApplicationSetCanQuit (int state) 795{ 796 NSNumber *n; 797 798 n = [[NSNumber alloc] initWithBool:state]; 799 800 message_kit_thread (@selector (set_can_quit:), n); 801 802 [n release]; 803} 804 805void 806X11ApplicationServerReady (void) 807{ 808 message_kit_thread (@selector (server_ready:), nil); 809} 810 811void 812X11ApplicationShowHideMenubar (int state) 813{ 814 NSNumber *n; 815 816 n = [[NSNumber alloc] initWithBool:state]; 817 818 message_kit_thread (@selector (show_hide_menubar:), n); 819 820 [n release]; 821} 822 823static void * 824create_thread (void *func, void *arg) 825{ 826 pthread_attr_t attr; 827 pthread_t tid; 828 829 pthread_attr_init (&attr); 830 831 pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); 832 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 833 834 pthread_create (&tid, &attr, func, arg); 835 836 pthread_attr_destroy (&attr); 837 838 return (void *) tid; 839} 840 841static void 842check_xinitrc (void) 843{ 844 char *tem, buf[1024]; 845 NSString *msg; 846 847 if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO]) 848 return; 849 850 tem = getenv ("HOME"); 851 if (tem == NULL) 852 goto done; 853 854 snprintf (buf, sizeof (buf), "%s/.xinitrc", tem); 855 if (access (buf, F_OK) != 0) 856 goto done; 857 858 /* FIXME: put localized strings into Resources/English.lproj */ 859 860 msg = NSLocalizedString ( 861@"You have an existing ~/.xinitrc file.\n\n\ 862Windows displayed by X11 applications may not have titlebars, or may look \ 863different to windows displayed by native applications.\n\n\ 864Would you like to move aside the existing file and use the standard X11 \ 865environment?", @""); 866 867 if (NSRunAlertPanel (nil, msg, NSLocalizedString (@"Yes", @""), 868 NSLocalizedString (@"No", @""), nil) 869 == NSAlertDefaultReturn) 870 { 871 char buf2[1024]; 872 int i = -1; 873 874 snprintf (buf2, sizeof (buf2), "%s.old", buf); 875 876 for (i = 1; access (buf2, F_OK) == 0; i++) 877 snprintf (buf2, sizeof (buf2), "%s.old.%d", buf, i); 878 879 rename (buf, buf2); 880 } 881 882done: 883 [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES]; 884 [X11App prefs_synchronize]; 885} 886 887void 888X11ApplicationMain (int argc, const char *argv[], 889 void (*server_thread) (void *), void *server_arg) 890{ 891 NSAutoreleasePool *pool; 892 893#ifdef DEBUG 894 while (access ("/tmp/x11-block", F_OK) == 0) 895 sleep (1); 896#endif 897 898 pool = [[NSAutoreleasePool alloc] init]; 899 900 X11App = (X11Application *) [X11Application sharedApplication]; 901 902 init_ports (); 903 904 [NSApp read_defaults]; 905 906 [NSBundle loadNibNamed:@"main" owner:NSApp]; 907 908 [[NSNotificationCenter defaultCenter] addObserver:NSApp 909 selector:@selector (became_key:) 910 name:NSWindowDidBecomeKeyNotification object:nil]; 911 912 check_xinitrc (); 913 914 if (!create_thread (server_thread, server_arg)) 915 { 916 fprintf (stderr, "can't create secondary thread\n"); 917 exit (1); 918 } 919 920 [NSApp run]; 921 922 /* not reached */ 923} 924 925 926/* event conversion */ 927 928static inline unsigned short 929convert_flags (unsigned int nsflags) 930{ 931 unsigned int xflags; 932 933 if (nsflags == ~0) 934 return 0xffff; 935 936 xflags = 0; 937 938 if (nsflags & NSAlphaShiftKeyMask) 939 xflags |= LockMask; 940 if (nsflags & NSShiftKeyMask) 941 xflags |= ShiftMask; 942 if (nsflags & NSControlKeyMask) 943 xflags |= ControlMask; 944 if (nsflags & NSAlternateKeyMask) 945 xflags |= Mod1Mask; 946 if (nsflags & NSCommandKeyMask) 947 xflags |= Mod2Mask; 948 /* FIXME: secondaryfn? */ 949 950 return xflags; 951} 952 953static void 954send_nsevent (NSEventType type, NSEvent *e) 955{ 956 static unsigned int button_state = 0; 957 958 xEvent xe; 959 960 memset (&xe, 0, sizeof (xe)); 961 962 switch (type) 963 { 964 NSRect screen; 965 NSPoint location; 966 NSWindow *window; 967 int pointer_x, pointer_y, count; 968 969 case NSLeftMouseDown: 970 xe.u.u.type = ButtonPress; 971 xe.u.u.detail = 1; 972 goto do_press_event; 973 974 case NSRightMouseDown: 975 xe.u.u.type = ButtonPress; 976 xe.u.u.detail = 3; 977 goto do_press_event; 978 979 case NSOtherMouseDown: 980 xe.u.u.type = ButtonPress; 981 xe.u.u.detail = 2; /* FIXME? */ 982 goto do_press_event; 983 984 do_press_event: 985 if (RootlessKnowsWindowNumber ([e windowNumber]) == NULL) 986 { 987 /* X server doesn't grok this window, drop the event. 988 989 Note: theoretically this isn't necessary, but if I click 990 on the menubar, we get sent a LeftMouseDown when the 991 release happens, but no LeftMouseUp is ever seen! */ 992 993 break; 994 } 995 goto do_event; 996 997 case NSLeftMouseUp: 998 xe.u.u.type = ButtonRelease; 999 xe.u.u.detail = 1; 1000 goto do_release_event; 1001 1002 case NSRightMouseUp: 1003 xe.u.u.type = ButtonRelease; 1004 xe.u.u.detail = 3; 1005 goto do_release_event; 1006 1007 case NSOtherMouseUp: 1008 xe.u.u.type = ButtonRelease; 1009 xe.u.u.detail = 2; /* FIXME? */ 1010 goto do_release_event; 1011 1012 do_release_event: 1013 if ((button_state & (1 << xe.u.u.detail)) == 0) 1014 { 1015 /* X didn't see the button press for this release, so skip it */ 1016 break; 1017 } 1018 goto do_event; 1019 1020 case NSMouseMoved: 1021 case NSLeftMouseDragged: 1022 case NSRightMouseDragged: 1023 case NSOtherMouseDragged: 1024 /* convert location to global top-left coordinates */ 1025 1026 location = [e locationInWindow]; 1027 window = [e window]; 1028 screen = [[[NSScreen screens] objectAtIndex:0] frame]; 1029 1030 if (window != nil) 1031 { 1032 NSRect frame = [window frame]; 1033 pointer_x = location.x + frame.origin.x; 1034 pointer_y = (((screen.origin.y + screen.size.height) 1035 - location.y) - frame.origin.y); 1036 } 1037 else 1038 { 1039 pointer_x = location.x; 1040 pointer_y = (screen.origin.y + screen.size.height) - location.y; 1041 } 1042 1043 xe.u.keyButtonPointer.rootX = pointer_x; 1044 xe.u.keyButtonPointer.rootY = pointer_y; 1045 xe.u.u.type = MotionNotify; 1046 goto do_event; 1047 1048 case NSKeyDown: 1049 xe.u.u.type = KeyPress; 1050 xe.u.u.detail = [e keyCode]; 1051 goto do_event; 1052 1053 case NSKeyUp: 1054 xe.u.u.type = KeyRelease; 1055 xe.u.u.detail = [e keyCode]; 1056 goto do_event; 1057 1058 case NSScrollWheel: 1059 xe.u.keyButtonPointer.state = convert_flags ([e modifierFlags]); 1060 count = [e deltaY]; 1061 xe.u.u.detail = count > 0 ? 4 : 5; 1062 for (count = abs (count); count-- > 0;) 1063 { 1064 xe.u.u.type = ButtonPress; 1065 DarwinEnqueueEvent (&xe); 1066 xe.u.u.type = ButtonRelease; 1067 DarwinEnqueueEvent (&xe); 1068 } 1069 xe.u.u.type = 0; 1070 break; 1071 1072 case NSFlagsChanged: 1073 do_event: 1074 xe.u.keyButtonPointer.state = convert_flags ([e modifierFlags]); 1075 DarwinEnqueueEvent (&xe); 1076 break; 1077 1078 default: break; /* for gcc */ 1079 } 1080 1081 if (xe.u.u.type == ButtonPress) 1082 button_state |= (1 << xe.u.u.detail); 1083 else if (xe.u.u.type == ButtonRelease) 1084 button_state &= ~(1 << xe.u.u.detail); 1085} 1086