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