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