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