1 /* X11Application.m -- subclass of NSApplication to multiplex events
3 * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
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:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
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.
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.
31 #include "sanitizedCarbon.h"
33 #ifdef HAVE_DIX_CONFIG_H
34 #include <dix-config.h>
37 #include "quartzCommon.h"
39 #import "X11Application.h"
43 #include "darwinEvents.h"
44 #include "quartzKeyboard.h"
45 #include <X11/extensions/applewmconst.h>
47 #include "exglobals.h"
49 #include <mach/mach.h>
51 #include <AvailabilityMacros.h>
61 #define DEFAULTS_FILE X11LIBDIR "/X11/xserver/Xquartz.plist"
63 #ifndef XSERVER_VERSION
64 #define XSERVER_VERSION "?"
67 #ifdef HAVE_LIBDISPATCH
68 #include <dispatch/dispatch.h>
70 static dispatch_queue_t eventTranslationQueue;
74 #define __has_feature(x) 0
77 #ifndef CF_RETURNS_RETAINED
78 #if __has_feature(attribute_cf_returns_retained)
79 #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
81 #define CF_RETURNS_RETAINED
85 extern Bool noTestExtensions;
86 extern Bool noRenderExtension;
87 extern BOOL serverRunning;
89 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
90 static TISInputSourceRef last_key_layout;
92 static KeyboardLayoutRef last_key_layout;
95 /* This preference is only tested on Lion or later as it's not relevant to
96 * earlier OS versions.
98 Bool XQuartzScrollInDeviceDirection = FALSE;
100 extern int darwinFakeButtons;
102 /* Store the mouse location while in the background, and update X11's pointer
103 * location when we become the foreground application
105 static NSPoint bgMouseLocation;
106 static BOOL bgMouseLocationUpdated = FALSE;
108 X11Application *X11App;
110 CFStringRef app_prefs_domain_cfstr = NULL;
112 #define ALL_KEY_MASKS (NSShiftKeyMask | NSControlKeyMask | \
113 NSAlternateKeyMask | NSCommandKeyMask)
115 @interface X11Application (Private)
116 - (void) sendX11NSEvent:(NSEvent *)e;
119 @implementation X11Application
121 typedef struct message_struct message;
122 struct message_struct {
123 mach_msg_header_t hdr;
128 static mach_port_t _port;
130 /* Quartz mode initialization routine. This is often dynamically loaded
131 but is statically linked into this X server. */
133 QuartzModeBundleInit(void);
141 if (_port != MACH_PORT_NULL) return;
143 r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_port);
144 if (r != KERN_SUCCESS) return;
146 p = [NSMachPort portWithMachPort:_port];
147 [p setDelegate:NSApp];
148 [p scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:
149 NSDefaultRunLoopMode];
153 message_kit_thread(SEL selector, NSObject *arg)
158 msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
159 msg.hdr.msgh_size = sizeof(msg);
160 msg.hdr.msgh_remote_port = _port;
161 msg.hdr.msgh_local_port = MACH_PORT_NULL;
162 msg.hdr.msgh_reserved = 0;
165 msg.selector = selector;
166 msg.arg = [arg retain];
168 r = mach_msg(&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size,
169 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
170 if (r != KERN_SUCCESS)
171 ErrorF("%s: mach_msg failed: %x\n", __FUNCTION__, r);
174 - (void) handleMachMessage:(void *)_msg
178 [self performSelector:msg->selector withObject:msg->arg];
182 - (void) set_controller:obj
184 if (_controller == nil) _controller = [obj retain];
189 if (_controller != nil) [_controller release];
191 if (_port != MACH_PORT_NULL)
192 mach_port_deallocate(mach_task_self(), _port);
197 - (void) orderFrontStandardAboutPanel: (id) sender
199 NSMutableDictionary *dict;
200 NSDictionary *infoDict;
203 dict = [NSMutableDictionary dictionaryWithCapacity:3];
204 infoDict = [[NSBundle mainBundle] infoDictionary];
206 [dict setObject: NSLocalizedString(@"The X Window System", @"About panel")
207 forKey:@"ApplicationName"];
209 tem = [infoDict objectForKey:@"CFBundleShortVersionString"];
211 [dict setObject:[NSString stringWithFormat:@"XQuartz %@", tem]
212 forKey:@"ApplicationVersion"];
214 [dict setObject:[NSString stringWithFormat:@"xorg-server %s",
218 [self orderFrontStandardAboutPanelWithOptions: dict];
221 - (void) activateX:(OSX_BOOL)state
223 if (_x_active == state)
226 DEBUG_LOG("state=%d, _x_active=%d, \n", state, _x_active);
228 if (bgMouseLocationUpdated) {
229 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
230 bgMouseLocation.x, bgMouseLocation.y,
232 bgMouseLocationUpdated = FALSE;
234 DarwinSendDDXEvent(kXquartzActivate, 0);
238 if (darwin_all_modifier_flags)
239 DarwinUpdateModKeys(0);
241 DarwinInputReleaseButtonsAndKeys(darwinKeyboard);
242 DarwinInputReleaseButtonsAndKeys(darwinPointer);
243 DarwinInputReleaseButtonsAndKeys(darwinTabletCursor);
244 DarwinInputReleaseButtonsAndKeys(darwinTabletStylus);
245 DarwinInputReleaseButtonsAndKeys(darwinTabletEraser);
247 DarwinSendDDXEvent(kXquartzDeactivate, 0);
253 - (void) became_key:(NSWindow *)win
258 - (void) sendEvent:(NSEvent *)e
260 OSX_BOOL for_appkit, for_x;
262 /* By default pass down the responder chain and to X. */
267 case NSLeftMouseDown:
268 case NSRightMouseDown:
269 case NSOtherMouseDown:
273 if ([e window] != nil) {
274 /* Pointer event has an (AppKit) window. Probably something for the kit. */
276 if (_x_active) [self activateX:NO];
278 else if ([self modalWindow] == nil) {
279 /* Must be an X window. Tell appkit it doesn't have focus. */
282 if ([self isActive]) {
284 if (!_x_active && quartzProcs->IsX11Window([e windowNumber]))
285 [self activateX:YES];
289 /* We want to force sending to appkit if we're over the menu bar */
291 NSPoint NSlocation = [e locationInWindow];
292 NSWindow *window = [e window];
293 NSRect NSframe, NSvisibleFrame;
294 CGRect CGframe, CGvisibleFrame;
298 NSRect frame = [window frame];
299 NSlocation.x += frame.origin.x;
300 NSlocation.y += frame.origin.y;
303 NSframe = [[NSScreen mainScreen] frame];
304 NSvisibleFrame = [[NSScreen mainScreen] visibleFrame];
306 CGframe = CGRectMake(NSframe.origin.x, NSframe.origin.y,
307 NSframe.size.width, NSframe.size.height);
308 CGvisibleFrame = CGRectMake(NSvisibleFrame.origin.x,
309 NSvisibleFrame.origin.y,
310 NSvisibleFrame.size.width,
311 NSvisibleFrame.size.height);
312 CGlocation = CGPointMake(NSlocation.x, NSlocation.y);
314 if (CGRectContainsPoint(CGframe, CGlocation) &&
315 !CGRectContainsPoint(CGvisibleFrame, CGlocation))
325 static BOOL do_swallow = NO;
326 static int swallow_keycode;
328 if ([e type] == NSKeyDown) {
329 /* Before that though, see if there are any global
330 * shortcuts bound to it. */
332 if (darwinAppKitModMask &[e modifierFlags]) {
333 /* Override to force sending to Appkit */
334 swallow_keycode = [e keyCode];
337 #if XPLUGIN_VERSION >= 1
339 else if (XQuartzEnableKeyEquivalents &&
340 xp_is_symbolic_hotkey_event([e eventRef])) {
341 swallow_keycode = [e keyCode];
346 else if (XQuartzEnableKeyEquivalents &&
347 [[self mainMenu] performKeyEquivalent:e]) {
348 swallow_keycode = [e keyCode];
353 else if (!XQuartzIsRootless
354 && ([e modifierFlags] & ALL_KEY_MASKS) ==
355 (NSCommandKeyMask | NSAlternateKeyMask)
356 && ([e keyCode] == 0 /*a*/ || [e keyCode] ==
358 /* We have this here to force processing fullscreen
359 * toggle even if XQuartzEnableKeyEquivalents is disabled */
360 swallow_keycode = [e keyCode];
364 DarwinSendDDXEvent(kXquartzToggleFullscreen, 0);
367 /* No kit window is focused, so send it to X. */
372 /* If we saw a key equivalent on the down, don't pass
373 * the up through to X. */
374 if (do_swallow && [e keyCode] == swallow_keycode) {
380 else { /* !_x_active */
386 /* Don't tell X11 about modifiers changing while it's not active */
391 case NSAppKitDefined:
392 switch ([e subtype]) {
393 static BOOL x_was_active = NO;
395 case NSApplicationActivatedEventType:
397 if ([e window] == nil && x_was_active) {
398 BOOL order_all_windows = YES, workspaces, ok;
401 /* FIXME: This is a hack to avoid passing the event to AppKit which
402 * would result in it raising one of its windows.
404 _appFlags._active = YES;
406 [self set_front_process:nil];
408 /* Get the Spaces preference for SwitchOnActivate */
409 (void)CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
411 CFPreferencesGetAppBooleanValue(CFSTR("workspaces"),
419 (void)CFPreferencesAppSynchronize(CFSTR(
420 ".GlobalPreferences"));
422 CFPreferencesGetAppBooleanValue(CFSTR(
423 "AppleSpacesSwitchOnActivate"),
425 ".GlobalPreferences"),
428 order_all_windows = YES;
431 /* TODO: In the workspaces && !AppleSpacesSwitchOnActivate case, the windows are ordered
432 * correctly, but we need to activate the top window on this space if there is
435 * If there are no active windows, and there are minimized windows, we should
436 * be restoring one of them.
438 if ([e data2] & 0x10) { // 0x10 (bfCPSOrderAllWindowsForward) is set when we use cmd-tab or the dock icon
439 DarwinSendDDXEvent(kXquartzBringAllToFront, 1,
445 case 18: /* ApplicationDidReactivate */
446 if (XQuartzFullscreenVisible) for_appkit = NO;
449 case NSApplicationDeactivatedEventType:
452 x_was_active = _x_active;
463 if (for_appkit) [super sendEvent:e];
466 #ifdef HAVE_LIBDISPATCH
467 dispatch_async(eventTranslationQueue, ^{
468 [self sendX11NSEvent:e];
471 [self sendX11NSEvent:e];
476 - (void) set_window_menu:(NSArray *)list
478 [_controller set_window_menu:list];
481 - (void) set_window_menu_check:(NSNumber *)n
483 [_controller set_window_menu_check:n];
486 - (void) set_apps_menu:(NSArray *)list
488 [_controller set_apps_menu:list];
491 - (void) set_front_process:unused
493 [NSApp activateIgnoringOtherApps:YES];
495 if ([self modalWindow] == nil)
496 [self activateX:YES];
499 - (void) set_can_quit:(NSNumber *)state
501 [_controller set_can_quit:[state boolValue]];
504 - (void) server_ready:unused
506 [_controller server_ready];
509 - (void) show_hide_menubar:(NSNumber *)state
511 /* Also shows/hides the dock */
512 if ([state boolValue])
513 SetSystemUIMode(kUIModeNormal, 0);
515 SetSystemUIMode(kUIModeAllHidden,
516 XQuartzFullscreenMenu ? kUIOptionAutoShowMenuBar : 0); // kUIModeAllSuppressed or kUIOptionAutoShowMenuBar can be used to allow "mouse-activation"
519 - (void) launch_client:(NSString *)cmd
521 (void)[_controller application:self openFile:cmd];
524 /* user preferences */
526 /* Note that these functions only work for arrays whose elements
527 can be toll-free-bridged between NS and CF worlds. */
530 cfretain(CFAllocatorRef a, const void *b)
536 cfrelease(CFAllocatorRef a, const void *b)
542 static CFMutableArrayRef
543 nsarray_to_cfarray(NSArray *in)
545 CFMutableArrayRef out;
551 memset(&cb, 0, sizeof(cb));
553 cb.retain = cfretain;
554 cb.release = cfrelease;
557 out = CFArrayCreateMutable(NULL, count, &cb);
559 for (i = 0; i < count; i++) {
560 ns = [in objectAtIndex:i];
562 if ([ns isKindOfClass:[NSArray class]])
563 cf = (CFTypeRef)nsarray_to_cfarray((NSArray *)ns);
565 cf = CFRetain((CFTypeRef)ns);
567 CFArrayAppendValue(out, cf);
574 static NSMutableArray *
575 cfarray_to_nsarray(CFArrayRef in)
582 count = CFArrayGetCount(in);
583 out = [[NSMutableArray alloc] initWithCapacity:count];
585 for (i = 0; i < count; i++) {
586 cf = CFArrayGetValueAtIndex(in, i);
588 if (CFGetTypeID(cf) == CFArrayGetTypeID())
589 ns = cfarray_to_nsarray((CFArrayRef)cf);
591 ns = [(id) cf retain];
600 - (CFPropertyListRef) prefs_get_copy:(NSString *)key
602 CFPropertyListRef value;
604 value = CFPreferencesCopyAppValue((CFStringRef)key,
605 app_prefs_domain_cfstr);
608 static CFDictionaryRef defaults;
610 if (defaults == NULL) {
611 CFStringRef error = NULL;
616 url = (CFURLCreateFromFileSystemRepresentation
617 (NULL, (unsigned char *)DEFAULTS_FILE,
618 strlen(DEFAULTS_FILE), false));
619 if (CFURLCreateDataAndPropertiesFromResource(NULL, url, &data,
622 defaults = (CFPropertyListCreateFromXMLData
624 kCFPropertyListMutableContainersAndLeaves,
626 if (error != NULL) CFRelease(error);
631 if (defaults != NULL) {
632 NSMutableArray *apps, *elt;
634 NSString *name, *nname;
636 /* Localize the names in the default apps menu. */
639 [(NSDictionary *) defaults objectForKey:@PREFS_APPSMENU];
641 count = [apps count];
642 for (i = 0; i < count; i++) {
643 elt = [apps objectAtIndex:i];
645 [elt isKindOfClass:[NSArray class]]) {
646 name = [elt objectAtIndex:0];
648 nname = NSLocalizedString(name, nil);
649 if (nname != nil && nname != name)
650 [elt replaceObjectAtIndex:0 withObject:
659 if (defaults != NULL) value = CFDictionaryGetValue(defaults, key);
660 if (value != NULL) CFRetain(value);
666 - (int) prefs_get_integer:(NSString *)key default:(int)def
668 CFPropertyListRef value;
671 value = [self prefs_get_copy:key];
673 if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID())
674 CFNumberGetValue(value, kCFNumberIntType, &ret);
675 else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
676 ret = CFStringGetIntValue(value);
680 if (value != NULL) CFRelease(value);
685 - (const char *) prefs_get_string:(NSString *)key default:(const char *)def
687 CFPropertyListRef value;
688 const char *ret = NULL;
690 value = [self prefs_get_copy:key];
692 if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
693 NSString *s = (NSString *)value;
695 ret = [s UTF8String];
698 if (value != NULL) CFRelease(value);
700 return ret != NULL ? ret : def;
703 - (NSURL *) prefs_copy_url:(NSString *)key default:(NSURL *)def
705 CFPropertyListRef value;
708 value = [self prefs_get_copy:key];
710 if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID()) {
711 NSString *s = (NSString *)value;
713 ret = [NSURL URLWithString:s];
717 if (value != NULL) CFRelease(value);
719 return ret != NULL ? ret : def;
722 - (float) prefs_get_float:(NSString *)key default:(float)def
724 CFPropertyListRef value;
727 value = [self prefs_get_copy:key];
730 && CFGetTypeID(value) == CFNumberGetTypeID()
731 && CFNumberIsFloatType(value))
732 CFNumberGetValue(value, kCFNumberFloatType, &ret);
733 else if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID())
734 ret = CFStringGetDoubleValue(value);
736 if (value != NULL) CFRelease(value);
741 - (int) prefs_get_boolean:(NSString *)key default:(int)def
743 CFPropertyListRef value;
746 value = [self prefs_get_copy:key];
749 if (CFGetTypeID(value) == CFNumberGetTypeID())
750 CFNumberGetValue(value, kCFNumberIntType, &ret);
751 else if (CFGetTypeID(value) == CFBooleanGetTypeID())
752 ret = CFBooleanGetValue(value);
753 else if (CFGetTypeID(value) == CFStringGetTypeID()) {
754 const char *tem = [(NSString *) value UTF8String];
755 if (strcasecmp(tem, "true") == 0 || strcasecmp(tem, "yes") == 0)
766 - (NSArray *) prefs_get_array:(NSString *)key
769 CFPropertyListRef value;
771 value = [self prefs_get_copy:key];
774 if (CFGetTypeID(value) == CFArrayGetTypeID())
775 ret = [cfarray_to_nsarray (value)autorelease];
783 - (void) prefs_set_integer:(NSString *)key value:(int)value
787 x = CFNumberCreate(NULL, kCFNumberIntType, &value);
789 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
790 app_prefs_domain_cfstr,
791 kCFPreferencesCurrentUser,
792 kCFPreferencesAnyHost);
797 - (void) prefs_set_float:(NSString *)key value:(float)value
801 x = CFNumberCreate(NULL, kCFNumberFloatType, &value);
803 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)x,
804 app_prefs_domain_cfstr,
805 kCFPreferencesCurrentUser,
806 kCFPreferencesAnyHost);
811 - (void) prefs_set_boolean:(NSString *)key value:(int)value
813 CFPreferencesSetValue(
815 (CFTypeRef)(value ? kCFBooleanTrue
817 app_prefs_domain_cfstr,
818 kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
822 - (void) prefs_set_array:(NSString *)key value:(NSArray *)value
826 cfarray = nsarray_to_cfarray(value);
827 CFPreferencesSetValue((CFStringRef)key,
829 app_prefs_domain_cfstr,
830 kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
834 - (void) prefs_set_string:(NSString *)key value:(NSString *)value
836 CFPreferencesSetValue((CFStringRef)key, (CFTypeRef)value,
837 app_prefs_domain_cfstr, kCFPreferencesCurrentUser,
838 kCFPreferencesAnyHost);
841 - (void) prefs_synchronize
843 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
846 - (void) read_defaults
851 XQuartzRootlessDefault = [self prefs_get_boolean:@PREFS_ROOTLESS
852 default :XQuartzRootlessDefault];
853 XQuartzFullscreenMenu = [self prefs_get_boolean:@PREFS_FULLSCREEN_MENU
854 default :XQuartzFullscreenMenu];
855 XQuartzFullscreenDisableHotkeys =
856 ![self prefs_get_boolean:@PREFS_FULLSCREEN_HOTKEYS
858 XQuartzFullscreenDisableHotkeys];
859 darwinFakeButtons = [self prefs_get_boolean:@PREFS_FAKEBUTTONS
860 default :darwinFakeButtons];
861 XQuartzOptionSendsAlt = [self prefs_get_boolean:@PREFS_OPTION_SENDS_ALT
862 default :XQuartzOptionSendsAlt];
864 if (darwinFakeButtons) {
865 const char *fake2, *fake3;
867 fake2 = [self prefs_get_string:@PREFS_FAKE_BUTTON2 default:NULL];
868 fake3 = [self prefs_get_string:@PREFS_FAKE_BUTTON3 default:NULL];
870 if (fake2 != NULL) darwinFakeMouse2Mask = DarwinParseModifierList(
872 if (fake3 != NULL) darwinFakeMouse3Mask = DarwinParseModifierList(
876 tem = [self prefs_get_string:@PREFS_APPKIT_MODIFIERS default:NULL];
877 if (tem != NULL) darwinAppKitModMask = DarwinParseModifierList(tem, TRUE);
879 tem = [self prefs_get_string:@PREFS_WINDOW_ITEM_MODIFIERS default:NULL];
881 windowItemModMask = DarwinParseModifierList(tem, FALSE);
884 nsstr = NSLocalizedString(@"window item modifiers",
885 @"window item modifiers");
887 tem = [nsstr UTF8String];
888 if ((tem != NULL) && strcmp(tem, "window item modifiers")) {
889 windowItemModMask = DarwinParseModifierList(tem, FALSE);
894 XQuartzEnableKeyEquivalents = [self prefs_get_boolean:@PREFS_KEYEQUIVS
896 XQuartzEnableKeyEquivalents];
898 darwinSyncKeymap = [self prefs_get_boolean:@PREFS_SYNC_KEYMAP
899 default :darwinSyncKeymap];
901 darwinDesiredDepth = [self prefs_get_integer:@PREFS_DEPTH
902 default :darwinDesiredDepth];
904 noTestExtensions = ![self prefs_get_boolean:@PREFS_TEST_EXTENSIONS
907 noRenderExtension = ![self prefs_get_boolean:@PREFS_RENDER_EXTENSION
910 XQuartzScrollInDeviceDirection =
911 [self prefs_get_boolean:@PREFS_SCROLL_IN_DEV_DIRECTION
913 XQuartzScrollInDeviceDirection];
916 NSURL *url = [self prefs_copy_url:@PREFS_UPDATE_FEED default:nil];
918 [[SUUpdater sharedUpdater] setFeedURL:url];
924 /* This will end up at the end of the responder chain. */
927 DarwinSendDDXEvent(kXquartzPasteboardNotify, 1,
928 AppleWMCopyToPasteboard);
931 - (X11Controller *) controller
936 - (OSX_BOOL) x_active
944 array_with_strings_and_numbers(int nitems, const char **items,
947 NSMutableArray *array, *subarray;
948 NSString *string, *number;
951 /* (Can't autorelease on the X server thread) */
953 array = [[NSMutableArray alloc] initWithCapacity:nitems];
955 for (i = 0; i < nitems; i++) {
956 subarray = [[NSMutableArray alloc] initWithCapacity:2];
958 string = [[NSString alloc] initWithUTF8String:items[i]];
959 [subarray addObject:string];
962 if (numbers[i] != 0) {
963 number = [[NSString alloc] initWithFormat:@"%d", numbers[i]];
964 [subarray addObject:number];
968 [subarray addObject:@""];
970 [array addObject:subarray];
978 X11ApplicationSetWindowMenu(int nitems, const char **items,
979 const char *shortcuts)
982 array = array_with_strings_and_numbers(nitems, items, shortcuts);
984 /* Send the array of strings over to the appkit thread */
986 message_kit_thread(@selector (set_window_menu:), array);
991 X11ApplicationSetWindowMenuCheck(int idx)
995 n = [[NSNumber alloc] initWithInt:idx];
997 message_kit_thread(@selector (set_window_menu_check:), n);
1003 X11ApplicationSetFrontProcess(void)
1005 message_kit_thread(@selector (set_front_process:), nil);
1009 X11ApplicationSetCanQuit(int state)
1013 n = [[NSNumber alloc] initWithBool:state];
1015 message_kit_thread(@selector (set_can_quit:), n);
1021 X11ApplicationServerReady(void)
1023 message_kit_thread(@selector (server_ready:), nil);
1027 X11ApplicationShowHideMenubar(int state)
1031 n = [[NSNumber alloc] initWithBool:state];
1033 message_kit_thread(@selector (show_hide_menubar:), n);
1039 X11ApplicationLaunchClient(const char *cmd)
1043 string = [[NSString alloc] initWithUTF8String:cmd];
1045 message_kit_thread(@selector (launch_client:), string);
1050 /* This is a special function in that it is run from the *SERVER* thread and
1051 * not the AppKit thread. We want to block entering a screen-capturing RandR
1052 * mode until we notify the user about how to get out if the X11 client crashes.
1055 X11ApplicationCanEnterRandR(void)
1057 NSString *title, *msg;
1059 if ([X11App prefs_get_boolean:@PREFS_NO_RANDR_ALERT default:NO] ||
1060 XQuartzShieldingWindowLevel != 0)
1063 title = NSLocalizedString(@"Enter RandR mode?",
1064 @"Dialog title when switching to RandR");
1065 msg = NSLocalizedString(
1066 @"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.",
1067 @"Dialog when switching to RandR");
1069 if (!XQuartzIsRootless)
1070 QuartzShowFullscreen(FALSE);
1072 switch (NSRunAlertPanel(title, msg,
1073 NSLocalizedString(@"Allow",
1075 NSLocalizedString(@"Cancel",
1077 NSLocalizedString(@"Always Allow", @""))) {
1078 case NSAlertOtherReturn:
1079 [X11App prefs_set_boolean:@PREFS_NO_RANDR_ALERT value:YES];
1080 [X11App prefs_synchronize];
1082 case NSAlertDefaultReturn:
1091 X11ApplicationFatalError(const char *f, va_list args)
1093 #ifdef HAVE_LIBDISPATCH
1094 NSString *title, *msg;
1097 /* This is called by FatalError() in the server thread just before
1098 * we would abort. If the server never got off the ground, We should
1099 * inform the user of the error rather than letting the ever-so-friendly
1100 * CrashReporter do it for us.
1102 * This also has the benefit of forcing user interaction rather than
1103 * allowing an infinite throttled-restart if the crash occurs before
1104 * we can drain the launchd socket.
1107 if (serverRunning) {
1111 title = NSLocalizedString(@"The application X11 could not be opened.",
1112 @"Dialog title when encountering a fatal error");
1113 msg = NSLocalizedString(
1114 @"An error occurred while starting the X11 server: \"%s\"\n\nClick Quit to quit X11. Click Report to see more details or send a report to Apple.",
1115 @"Dialog when encountering a fatal error");
1117 vasprintf(&error_msg, f, args);
1118 msg = [NSString stringWithFormat:msg, error_msg];
1120 /* We want the AppKit thread to actually service the alert or we will race [NSApp run] and create an
1121 * 'NSInternalInconsistencyException', reason: 'NSApp with wrong _running count'
1123 dispatch_sync(dispatch_get_main_queue(), ^{
1124 if (NSAlertDefaultReturn ==
1125 NSRunAlertPanel (title, msg,
1126 NSLocalizedString (@"Quit", @""),
1128 @"Report...", @""), nil)) {
1129 exit (EXIT_FAILURE);
1133 /* fall back to caller to do the abort() in the DIX */
1140 char *tem, buf[1024];
1143 if ([X11App prefs_get_boolean:@PREFS_DONE_XINIT_CHECK default:NO])
1146 tem = getenv("HOME");
1147 if (tem == NULL) goto done;
1149 snprintf(buf, sizeof(buf), "%s/.xinitrc", tem);
1150 if (access(buf, F_OK) != 0)
1155 @"You have an existing ~/.xinitrc file.\n\n\
1156 Windows displayed by X11 applications may not have titlebars, or may look \
1157 different to windows displayed by native applications.\n\n\
1158 Would you like to move aside the existing file and use the standard X11 \
1159 environment the next time you start X11?" ,
1160 @"Startup xinitrc dialog");
1162 if (NSAlertDefaultReturn ==
1163 NSRunAlertPanel(nil, msg, NSLocalizedString(@"Yes", @""),
1164 NSLocalizedString(@"No",
1169 snprintf(buf2, sizeof(buf2), "%s.old", buf);
1171 for (i = 1; access(buf2, F_OK) == 0; i++)
1172 snprintf(buf2, sizeof(buf2), "%s.old.%d", buf, i);
1178 [X11App prefs_set_boolean:@PREFS_DONE_XINIT_CHECK value:YES];
1179 [X11App prefs_synchronize];
1182 static inline pthread_t
1183 create_thread(void *(*func)(void *), void *arg)
1185 pthread_attr_t attr;
1188 pthread_attr_init(&attr);
1189 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
1190 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1191 pthread_create(&tid, &attr, func, arg);
1192 pthread_attr_destroy(&attr);
1198 xpbproxy_x_thread(void *args)
1202 ErrorF("xpbproxy thread is terminating unexpectedly.\n");
1207 X11ApplicationMain(int argc, char **argv, char **envp)
1209 NSAutoreleasePool *pool;
1212 while (access("/tmp/x11-block", F_OK) == 0) sleep(1);
1215 pool = [[NSAutoreleasePool alloc] init];
1216 X11App = (X11Application *)[X11Application sharedApplication];
1219 app_prefs_domain_cfstr =
1220 (CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
1222 if (app_prefs_domain_cfstr == NULL) {
1224 "X11ApplicationMain: Unable to determine bundle identifier. Your installation of XQuartz may be broken.\n");
1225 app_prefs_domain_cfstr = CFSTR(BUNDLE_ID_PREFIX ".X11");
1228 [NSApp read_defaults];
1229 [NSBundle loadNibNamed:@"main" owner:NSApp];
1230 [[NSNotificationCenter defaultCenter] addObserver:NSApp
1231 selector:@selector (became_key:)
1233 NSWindowDidBecomeKeyNotification object:nil];
1236 * The xpr Quartz mode is statically linked into this server.
1237 * Initialize all the Quartz functions.
1239 QuartzModeBundleInit();
1241 /* Calculate the height of the menubar so we can avoid it. */
1242 aquaMenuBarHeight = NSHeight([[NSScreen mainScreen] frame]) -
1243 NSMaxY([[NSScreen mainScreen] visibleFrame]);
1245 #ifdef HAVE_LIBDISPATCH
1246 eventTranslationQueue = dispatch_queue_create(
1247 BUNDLE_ID_PREFIX ".X11.NSEventsToX11EventsQueue", NULL);
1248 assert(eventTranslationQueue != NULL);
1251 /* Set the key layout seed before we start the server */
1252 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
1253 last_key_layout = TISCopyCurrentKeyboardLayoutInputSource();
1255 if (!last_key_layout)
1257 "X11ApplicationMain: Unable to determine TISCopyCurrentKeyboardLayoutInputSource() at startup.\n");
1259 KLGetCurrentKeyboardLayout(&last_key_layout);
1260 if (!last_key_layout)
1262 "X11ApplicationMain: Unable to determine KLGetCurrentKeyboardLayout() at startup.\n");
1265 if (!QuartsResyncKeymap(FALSE)) {
1266 ErrorF("X11ApplicationMain: Could not build a valid keymap.\n");
1269 /* Tell the server thread that it can proceed */
1270 QuartzInitServer(argc, argv, envp);
1272 /* This must be done after QuartzInitServer because it can result in
1273 * an mieqEnqueue() - <rdar://problem/6300249>
1277 create_thread(xpbproxy_x_thread, NULL);
1280 [[X11App controller] setup_sparkle];
1281 [[SUUpdater sharedUpdater] resetUpdateCycle];
1282 // [[SUUpdater sharedUpdater] checkForUpdates:X11App];
1290 @implementation X11Application (Private)
1292 #ifdef NX_DEVICELCMDKEYMASK
1293 /* This is to workaround a bug in the VNC server where we sometimes see the L
1294 * modifier and sometimes see no "side"
1297 ensure_flag(int flags, int device_independent, int device_dependents,
1298 int device_dependent_default)
1300 if ((flags & device_independent) &&
1301 !(flags & device_dependents))
1302 flags |= device_dependent_default;
1307 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
1309 untrusted_str(NSEvent *e)
1313 return "NSScrollWheel";
1316 return "NSTabletPoint";
1318 case NSOtherMouseDown:
1319 return "NSOtherMouseDown";
1321 case NSOtherMouseUp:
1322 return "NSOtherMouseUp";
1324 case NSLeftMouseDown:
1325 return "NSLeftMouseDown";
1328 return "NSLeftMouseUp";
1331 switch ([e subtype]) {
1332 case NSTabletPointEventSubtype:
1333 return "NSTabletPointEventSubtype";
1335 case NSTabletProximityEventSubtype:
1336 return "NSTabletProximityEventSubtype";
1346 darwinEvents_lock(void);
1348 darwinEvents_unlock(void);
1350 - (void) sendX11NSEvent:(NSEvent *)e
1352 NSPoint location = NSZeroPoint;
1353 int ev_button, ev_type;
1354 static float pressure = 0.0; // static so ProximityOut will have the value from the previous tablet event
1355 static NSPoint tilt; // static so ProximityOut will have the value from the previous tablet event
1356 static DeviceIntPtr darwinTabletCurrent = NULL;
1357 static BOOL needsProximityIn = NO; // Do we do need to handle a pending ProximityIn once we have pressure/tilt?
1360 BOOL isMouseOrTabletEvent, isTabletEvent;
1362 if (!darwinTabletCurrent) {
1363 /* Ensure that the event system is initialized */
1364 darwinEvents_lock();
1365 darwinEvents_unlock();
1366 assert(darwinTabletStylus);
1369 darwinTabletCurrent = darwinTabletStylus;
1372 isMouseOrTabletEvent = [e type] == NSLeftMouseDown ||
1373 [e type] == NSOtherMouseDown ||
1374 [e type] == NSRightMouseDown ||
1375 [e type] == NSLeftMouseUp ||
1376 [e type] == NSOtherMouseUp ||
1377 [e type] == NSRightMouseUp ||
1378 [e type] == NSLeftMouseDragged ||
1379 [e type] == NSOtherMouseDragged ||
1380 [e type] == NSRightMouseDragged ||
1381 [e type] == NSMouseMoved ||
1382 [e type] == NSTabletPoint ||
1383 [e type] == NSScrollWheel;
1385 isTabletEvent = ([e type] == NSTabletPoint) ||
1386 (isMouseOrTabletEvent &&
1387 ([e subtype] == NSTabletPointEventSubtype ||
1388 [e subtype] == NSTabletProximityEventSubtype));
1390 if (isMouseOrTabletEvent) {
1391 static NSPoint lastpt;
1392 NSWindow *window = [e window];
1393 NSRect screen = [[[NSScreen screens] objectAtIndex:0] frame];
1394 BOOL hasUntrustedPointerDelta;
1396 // NSEvents for tablets are not consistent wrt deltaXY between events, so we cannot rely on that
1397 // Thus tablets will be subject to the warp-pointer bug worked around by the delta, but tablets
1398 // are not normally used in cases where that bug would present itself, so this is a fair tradeoff
1399 // <rdar://problem/7111003> deltaX and deltaY are incorrect for NSMouseMoved, NSTabletPointEventSubtype
1400 // http://xquartz.macosforge.org/trac/ticket/288
1401 hasUntrustedPointerDelta = isTabletEvent;
1403 // The deltaXY for middle click events also appear erroneous after fast user switching
1404 // <rdar://problem/7979468> deltaX and deltaY are incorrect for NSOtherMouseDown and NSOtherMouseUp after FUS
1405 // http://xquartz.macosforge.org/trac/ticket/389
1406 hasUntrustedPointerDelta |= [e type] == NSOtherMouseDown ||
1407 [e type] == NSOtherMouseUp;
1409 // The deltaXY for scroll events correspond to the scroll delta, not the pointer delta
1410 // <rdar://problem/7989690> deltaXY for wheel events are being sent as mouse movement
1411 hasUntrustedPointerDelta |= [e type] == NSScrollWheel;
1413 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
1414 hasUntrustedPointerDelta |= [e type] == NSLeftMouseDown ||
1415 [e type] == NSLeftMouseUp;
1418 if (window != nil) {
1419 NSRect frame = [window frame];
1420 location = [e locationInWindow];
1421 location.x += frame.origin.x;
1422 location.y += frame.origin.y;
1425 else if (hasUntrustedPointerDelta) {
1426 #ifdef DEBUG_UNTRUSTED_POINTER_DELTA
1427 ErrorF("--- Begin Event Debug ---\n");
1428 ErrorF("Event type: %s\n", untrusted_str(e));
1429 ErrorF("old lastpt: (%0.2f, %0.2f)\n", lastpt.x, lastpt.y);
1430 ErrorF(" delta: (%0.2f, %0.2f)\n", [e deltaX], -[e deltaY]);
1431 ErrorF(" location: (%0.2f, %0.2f)\n", lastpt.x + [e deltaX],
1432 lastpt.y - [e deltaY]);
1433 ErrorF("workaround: (%0.2f, %0.2f)\n", [e locationInWindow].x,
1434 [e locationInWindow].y);
1435 ErrorF("--- End Event Debug ---\n");
1437 location.x = lastpt.x + [e deltaX];
1438 location.y = lastpt.y - [e deltaY];
1439 lastpt = [e locationInWindow];
1441 location = [e locationInWindow];
1446 location.x = lastpt.x + [e deltaX];
1447 location.y = lastpt.y - [e deltaY];
1448 lastpt = [e locationInWindow];
1451 /* Convert coordinate system */
1452 location.y = (screen.origin.y + screen.size.height) - location.y;
1455 modifierFlags = [e modifierFlags];
1457 #ifdef NX_DEVICELCMDKEYMASK
1458 /* This is to workaround a bug in the VNC server where we sometimes see the L
1459 * modifier and sometimes see no "side"
1461 modifierFlags = ensure_flag(modifierFlags, NX_CONTROLMASK,
1462 NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK,
1463 NX_DEVICELCTLKEYMASK);
1464 modifierFlags = ensure_flag(modifierFlags, NX_SHIFTMASK,
1465 NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK,
1466 NX_DEVICELSHIFTKEYMASK);
1467 modifierFlags = ensure_flag(modifierFlags, NX_COMMANDMASK,
1468 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK,
1469 NX_DEVICELCMDKEYMASK);
1470 modifierFlags = ensure_flag(modifierFlags, NX_ALTERNATEMASK,
1471 NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK,
1472 NX_DEVICELALTKEYMASK);
1475 modifierFlags &= darwin_all_modifier_mask;
1477 /* We don't receive modifier key events while out of focus, and 3button
1478 * emulation mucks this up, so we need to check our modifier flag state
1479 * on every event... ugg
1482 if (darwin_all_modifier_flags != modifierFlags)
1483 DarwinUpdateModKeys(modifierFlags);
1486 case NSLeftMouseDown:
1488 ev_type = ButtonPress;
1491 case NSOtherMouseDown:
1493 ev_type = ButtonPress;
1496 case NSRightMouseDown:
1498 ev_type = ButtonPress;
1503 ev_type = ButtonRelease;
1506 case NSOtherMouseUp:
1508 ev_type = ButtonRelease;
1511 case NSRightMouseUp:
1513 ev_type = ButtonRelease;
1516 case NSLeftMouseDragged:
1518 ev_type = MotionNotify;
1521 case NSOtherMouseDragged:
1523 ev_type = MotionNotify;
1526 case NSRightMouseDragged:
1528 ev_type = MotionNotify;
1533 ev_type = MotionNotify;
1538 ev_type = MotionNotify;
1542 pDev = darwinPointer;
1544 /* NSTabletPoint can have no subtype */
1545 if ([e type] != NSTabletPoint &&
1546 [e subtype] == NSTabletProximityEventSubtype) {
1547 switch ([e pointingDeviceType]) {
1548 case NSEraserPointingDevice:
1549 darwinTabletCurrent = darwinTabletEraser;
1552 case NSPenPointingDevice:
1553 darwinTabletCurrent = darwinTabletStylus;
1556 case NSCursorPointingDevice:
1557 case NSUnknownPointingDevice:
1559 darwinTabletCurrent = darwinTabletCursor;
1563 if ([e isEnteringProximity])
1564 needsProximityIn = YES;
1566 DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
1567 location.x, location.y, pressure,
1572 if ([e type] == NSTabletPoint ||
1573 [e subtype] == NSTabletPointEventSubtype) {
1574 pressure = [e pressure];
1577 pDev = darwinTabletCurrent;
1579 if (needsProximityIn) {
1580 DarwinSendTabletEvents(darwinTabletCurrent, ProximityIn, 0,
1581 location.x, location.y, pressure,
1584 needsProximityIn = NO;
1588 if (!XQuartzServerVisible && noTestExtensions) {
1589 #if defined(XPLUGIN_VERSION) && XPLUGIN_VERSION > 0
1590 /* Older libXplugin (Tiger/"Stock" Leopard) aren't thread safe, so we can't call xp_find_window from the Appkit thread */
1591 xp_window_id wid = 0;
1594 /* Sigh. Need to check that we're really over one of
1595 * our windows. (We need to receive pointer events while
1596 * not in the foreground, but we don't want to receive them
1597 * when another window is over us or we might show a tooltip)
1600 err = xp_find_window(location.x, location.y, 0, &wid);
1602 if (err != XP_Success || (err == XP_Success && wid == 0))
1605 bgMouseLocation = location;
1606 bgMouseLocationUpdated = TRUE;
1611 if (bgMouseLocationUpdated) {
1612 if (!(ev_type == MotionNotify && ev_button == 0)) {
1613 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
1614 location.x, location.y,
1617 bgMouseLocationUpdated = FALSE;
1620 if (pDev == darwinPointer) {
1621 DarwinSendPointerEvents(pDev, ev_type, ev_button,
1622 location.x, location.y,
1623 [e deltaX], [e deltaY]);
1625 DarwinSendTabletEvents(pDev, ev_type, ev_button,
1626 location.x, location.y, pressure,
1632 case NSTabletProximity:
1633 switch ([e pointingDeviceType]) {
1634 case NSEraserPointingDevice:
1635 darwinTabletCurrent = darwinTabletEraser;
1638 case NSPenPointingDevice:
1639 darwinTabletCurrent = darwinTabletStylus;
1642 case NSCursorPointingDevice:
1643 case NSUnknownPointingDevice:
1645 darwinTabletCurrent = darwinTabletCursor;
1649 if ([e isEnteringProximity])
1650 needsProximityIn = YES;
1652 DarwinSendTabletEvents(darwinTabletCurrent, ProximityOut, 0,
1653 location.x, location.y, pressure,
1659 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
1660 float deltaX = [e deltaX];
1661 float deltaY = [e deltaY];
1662 BOOL isContinuous = NO;
1664 CGFloat deltaX = [e deltaX];
1665 CGFloat deltaY = [e deltaY];
1666 CGEventRef cge = [e CGEvent];
1668 CGEventGetIntegerValueField(cge, kCGScrollWheelEventIsContinuous);
1671 /* Scale the scroll value by line height */
1672 CGEventSourceRef source = CGEventCreateSourceFromEvent(cge);
1674 double lineHeight = CGEventSourceGetPixelsPerLine(source);
1677 /* There's no real reason for the 1/5 ratio here other than that
1678 * it feels like a good ratio after some testing.
1681 deltaX *= lineHeight / 5.0;
1682 deltaY *= lineHeight / 5.0;
1687 #if !defined(XPLUGIN_VERSION) || XPLUGIN_VERSION == 0
1688 /* If we're in the background, we need to send a MotionNotify event
1689 * first, since we aren't getting them on background mouse motion
1691 if (!XQuartzServerVisible && noTestExtensions) {
1692 bgMouseLocationUpdated = FALSE;
1693 DarwinSendPointerEvents(darwinPointer, MotionNotify, 0,
1694 location.x, location.y,
1698 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
1699 // TODO: Change 1117 to NSAppKitVersionNumber10_7 when it is defined
1700 if (NSAppKitVersionNumber >= 1117 &&
1701 XQuartzScrollInDeviceDirection &&
1702 [e isDirectionInvertedFromDevice]) {
1707 /* This hack is in place to better deal with "clicky" scroll wheels:
1708 * http://xquartz.macosforge.org/trac/ticket/562
1710 if (!isContinuous) {
1711 static NSTimeInterval lastScrollTime = 0.0;
1713 /* These store how much extra we have already scrolled.
1714 * ie, this is how much we ignore on the next event.
1716 static double deficit_x = 0.0;
1717 static double deficit_y = 0.0;
1719 /* If we have past a second since the last scroll, wipe the slate
1722 if ([e timestamp] - lastScrollTime > 1.0) {
1723 deficit_x = deficit_y = 0.0;
1725 lastScrollTime = [e timestamp];
1727 if (deltaX != 0.0) {
1728 /* If we changed directions, wipe the slate clean */
1729 if ((deficit_x < 0.0 && deltaX > 0.0) ||
1730 (deficit_x > 0.0 && deltaX < 0.0)) {
1734 /* Eat up the deficit, but ensure that something is
1737 if (fabs(deltaX) > fabs(deficit_x)) {
1738 deltaX -= deficit_x;
1741 deficit_x = ceil(deltaX) - deltaX;
1742 deltaX = ceil(deltaX);
1744 deficit_x = floor(deltaX) - deltaX;
1745 deltaX = floor(deltaX);
1748 deficit_x -= deltaX;
1756 deficit_x += deltaX;
1760 if (deltaY != 0.0) {
1761 /* If we changed directions, wipe the slate clean */
1762 if ((deficit_y < 0.0 && deltaY > 0.0) ||
1763 (deficit_y > 0.0 && deltaY < 0.0)) {
1767 /* Eat up the deficit, but ensure that something is
1770 if (fabs(deltaY) > fabs(deficit_y)) {
1771 deltaY -= deficit_y;
1774 deficit_y = ceil(deltaY) - deltaY;
1775 deltaY = ceil(deltaY);
1777 deficit_y = floor(deltaY) - deltaY;
1778 deltaY = floor(deltaY);
1781 deficit_y -= deltaY;
1789 deficit_y += deltaY;
1794 DarwinSendScrollEvents(deltaX, deltaY);
1801 /* XKB clobbers our keymap at startup, so we need to force it on the first keypress.
1802 * TODO: Make this less of a kludge.
1804 static int force_resync_keymap = YES;
1805 if (force_resync_keymap) {
1806 DarwinSendDDXEvent(kXquartzReloadKeymap, 0);
1807 force_resync_keymap = NO;
1811 if (darwinSyncKeymap) {
1812 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
1813 TISInputSourceRef key_layout =
1814 TISCopyCurrentKeyboardLayoutInputSource();
1815 TISInputSourceRef clear;
1816 if (CFEqual(key_layout, last_key_layout)) {
1817 CFRelease(key_layout);
1820 /* Swap/free thread-safely */
1821 clear = last_key_layout;
1822 last_key_layout = key_layout;
1825 KeyboardLayoutRef key_layout;
1826 KLGetCurrentKeyboardLayout(&key_layout);
1827 if (key_layout != last_key_layout) {
1828 last_key_layout = key_layout;
1830 /* Update keyInfo */
1831 if (!QuartsResyncKeymap(TRUE)) {
1833 "sendX11NSEvent: Could not build a valid keymap.\n");
1838 ev_type = ([e type] == NSKeyDown) ? KeyPress : KeyRelease;
1839 DarwinSendKeyboardEvents(ev_type, [e keyCode]);
1843 break; /* for gcc */