| diff -ur fltk-1.3.0r9619.org/configure.in fltk-1.3.0r9619/configure.in |
| --- fltk-1.3.0r9619.org/configure.in 2012-04-22 04:45:09.000000000 +0200 |
| +++ fltk-1.3.0r9619/configure.in 2012-06-18 13:47:33.290447462 +0200 |
| @@ -865,6 +865,8 @@ |
| Darwin*) |
| # MacOS X uses Cocoa for graphics. |
| LIBS="$LIBS -framework Cocoa" |
| + # And some Carbon for keyboard handling |
| + LIBS="$LIBS -framework Carbon" |
| |
| if test x$have_pthread = xyes; then |
| AC_DEFINE(HAVE_PTHREAD) |
| diff -ur fltk-1.3.0r9619.org/src/Fl_cocoa.mm fltk-1.3.0r9619/src/Fl_cocoa.mm |
| --- fltk-1.3.0r9619.org/src/Fl_cocoa.mm 2012-06-16 10:49:52.000000000 +0200 |
| +++ fltk-1.3.0r9619/src/Fl_cocoa.mm 2012-06-18 13:47:42.944910782 +0200 |
| @@ -53,6 +53,7 @@ |
| #include <math.h> |
| |
| #import <Cocoa/Cocoa.h> |
| +#import <Carbon/Carbon.h> |
| |
| #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h |
| #if defined(__LP64__) && __LP64__ |
| @@ -114,6 +115,8 @@ |
| extern Fl_Window* fl_xmousewin; |
| #endif |
| |
| +bool use_simple_keyboard = false; |
| + |
| enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; |
| |
| |
| @@ -130,6 +133,39 @@ |
| { |
| } |
| |
| +// Undocumented voodoo. Taken from Mozilla. |
| +#define ENABLE_ROMAN_KYBDS_ONLY -23 |
| + |
| +void fl_update_focus(void) |
| +{ |
| + Fl_Widget *focus; |
| + |
| + focus = Fl::grab(); |
| + if (!focus) |
| + focus = Fl::focus(); |
| + if (!focus) |
| + return; |
| + |
| + if (focus->simple_keyboard()) |
| + use_simple_keyboard = true; |
| + else |
| + use_simple_keyboard = false; |
| + |
| + // Force a "Roman" or "ASCII" keyboard, which both the Mozilla and |
| + // Safari people seem to think implies turning off advanced IME stuff |
| + // (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput |
| + // in Safari/Webcore). Should be good enough for us then... |
| +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) |
| + CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); |
| + TSMSetDocumentProperty(TSMGetActiveDocument(), |
| + kTSMDocumentEnabledInputSourcesPropertyTag, |
| + sizeof(CFArrayRef), &inputSources); |
| + CFRelease(inputSources); |
| +#else |
| + KeyScript(use_simple_keyboard ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds); |
| +#endif |
| +} |
| + |
| /* |
| * Mac keyboard lookup table |
| */ |
| @@ -908,6 +944,25 @@ |
| } |
| @end |
| |
| +static const char* cocoaDead2FLTK(const char *in) |
| +{ |
| + if (strcmp(in, "\140") == 0) // GRAVE ACCENT |
| + return "\314\200"; // COMBINING GRAVE ACCENT |
| + if (strcmp(in, "\302\264") == 0) // ACUTE ACCENT |
| + return "\314\201"; // COMBINING ACUTE ACCENT |
| + if (strcmp(in, "\136") == 0) // CIRCUMFLEX ACCENT |
| + return "\314\202"; // COMBINING CIRCUMFLEX ACCENT |
| + if (strcmp(in, "\176") == 0) // TILDE |
| + return "\314\203"; // COMBINING TILDE |
| + if (strcmp(in, "\302\250") == 0) // DIAERESIS |
| + return "\314\210"; // COMBINING DIAERESIS |
| + // FIXME: OS X dead key behaviour isn't documented and I don't have |
| + // any more keyboards to test with... |
| + |
| + // hope that OS X gave us something proper to begin with |
| + return in; |
| +} |
| + |
| /* |
| Handle cocoa keyboard events |
| Events during a character composition sequence: |
| @@ -1648,6 +1703,7 @@ |
| - (void)rightMouseDragged:(NSEvent *)theEvent; |
| - (void)otherMouseDragged:(NSEvent *)theEvent; |
| - (void)scrollWheel:(NSEvent *)theEvent; |
| ++ (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags; |
| - (BOOL)handleKeyDown:(NSEvent *)theEvent; |
| - (void)keyDown:(NSEvent *)theEvent; |
| - (void)keyUp:(NSEvent *)theEvent; |
| @@ -1726,6 +1782,130 @@ |
| - (void)scrollWheel:(NSEvent *)theEvent { |
| cocoaMouseWheelHandler(theEvent); |
| } |
| ++ (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags { |
| + const UCKeyboardLayout *layout; |
| + OSStatus err; |
| + |
| + layout = NULL; |
| + |
| +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) |
| + TISInputSourceRef keyboard; |
| + CFDataRef uchr; |
| + |
| + keyboard = TISCopyCurrentKeyboardInputSource(); |
| + uchr = (CFDataRef)TISGetInputSourceProperty(keyboard, |
| + kTISPropertyUnicodeKeyLayoutData); |
| + if (uchr == NULL) |
| + return nil; |
| + |
| + layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); |
| +#else |
| + KeyboardLayoutRef old_layout; |
| + int kind; |
| + |
| + err = KLGetCurrentKeyboardLayout(&old_layout); |
| + if (err != noErr) |
| + return nil; |
| + |
| + err = KLGetKeyboardLayoutProperty(old_layout, kKLKind, |
| + (const void**)&kind); |
| + if (err != noErr) |
| + return nil; |
| + |
| + // Old, crufty layout format? |
| + if (kind == kKLKCHRKind) { |
| + void *kchr_layout; |
| + |
| + UInt32 chars, state; |
| + char buf[3]; |
| + |
| + unichar result[16]; |
| + ByteCount in_len, out_len; |
| + |
| + err = KLGetKeyboardLayoutProperty(old_layout, kKLKCHRData, |
| + (const void**)&kchr_layout); |
| + if (err != noErr) |
| + return nil; |
| + |
| + state = 0; |
| + |
| + keyCode &= 0x7f; |
| + modifierFlags &= 0xff00; |
| + |
| + chars = KeyTranslate(kchr_layout, keyCode | modifierFlags, &state); |
| + |
| + buf[0] = (chars >> 16) & 0xff; |
| + buf[1] = chars & 0xff; |
| + buf[2] = '\0'; |
| + |
| + if (buf[0] == '\0') { |
| + buf[0] = buf[1]; |
| + buf[1] = '\0'; |
| + } |
| + |
| + // The data is now in some layout specific encoding. Need to convert |
| + // this to unicode. |
| + |
| + ScriptCode script; |
| + TextEncoding encoding; |
| + TECObjectRef converter; |
| + |
| + script = (ScriptCode)GetScriptManagerVariable(smKeyScript); |
| + |
| + err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, |
| + kTextRegionDontCare, NULL, |
| + &encoding); |
| + if (err != noErr) |
| + return nil; |
| + |
| + err = TECCreateConverter(&converter, encoding, kTextEncodingUnicodeV4_0); |
| + if (err != noErr) |
| + return nil; |
| + |
| + in_len = strlen(buf); |
| + out_len = sizeof(result); |
| + |
| + err = TECConvertText(converter, (ConstTextPtr)buf, in_len, &in_len, |
| + (TextPtr)result, out_len, &out_len); |
| + |
| + TECDisposeConverter(converter); |
| + |
| + if (err != noErr) |
| + return nil; |
| + |
| + return [NSString stringWithCharacters:result |
| + length:(out_len / sizeof(unichar))]; |
| + } |
| + |
| + if ((kind != kKLKCHRuchrKind) && (kind != kKLuchrKind)) |
| + return nil; |
| + |
| + err = KLGetKeyboardLayoutProperty(old_layout, kKLuchrData, |
| + (const void**)&layout); |
| + if (err != noErr) |
| + return nil; |
| +#endif |
| + |
| + if (layout == NULL) |
| + return nil; |
| + |
| + UInt32 dead_state; |
| + UniCharCount max_len, actual_len; |
| + UniChar string[255]; |
| + |
| + dead_state = 0; |
| + max_len = sizeof(string)/sizeof(*string); |
| + |
| + modifierFlags = (modifierFlags >> 8) & 0xff; |
| + |
| + err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, |
| + LMGetKbdType(), 0, &dead_state, max_len, &actual_len, |
| + string); |
| + if (err != noErr) |
| + return nil; |
| + |
| + return [NSString stringWithCharacters:string length:actual_len]; |
| +} |
| - (BOOL)handleKeyDown:(NSEvent *)theEvent { |
| //NSLog(@"handleKeyDown"); |
| fl_lock_function(); |
| @@ -1752,14 +1932,47 @@ |
| break; |
| } |
| } |
| - if (!no_text_key && !(Fl::e_state & FL_META) ) { |
| - // Don't send cmd-<key> to interpretKeyEvents because it beeps. |
| + if (!no_text_key) { |
| + // The simple keyboard model will ignore insertText, so we need to grab |
| + // the symbol directly from the event. Note that we still use setMarkedText. |
| + if (use_simple_keyboard) { |
| + NSString *simple_chars; |
| + UInt32 modifiers; |
| + |
| + // We want a "normal" symbol out of the event, which basically means |
| + // we only respect the shift and alt/altgr modifiers. Cocoa can help |
| + // us if we only wanted shift, but as we also want alt/altgr, we'll |
| + // have to do some lookup ourselves. This matches our behaviour on |
| + // other platforms. |
| + |
| + modifiers = 0; |
| + if ([theEvent modifierFlags] & NSAlphaShiftKeyMask) |
| + modifiers |= alphaLock; |
| + if ([theEvent modifierFlags] & NSShiftKeyMask) |
| + modifiers |= shiftKey; |
| + if ([theEvent modifierFlags] & NSAlternateKeyMask) |
| + modifiers |= optionKey; |
| + |
| + simple_chars = [FLView keyTranslate:[theEvent keyCode] |
| + withModifierFlags:modifiers]; |
| + if (simple_chars == nil) { |
| + // Something went wrong. Fall back to what Cocoa gave us... |
| + simple_chars = [theEvent charactersIgnoringModifiers]; |
| + } |
| + |
| + [FLView prepareEtext:simple_chars]; |
| + } |
| + |
| // Then we can let the OS have a stab at it and see if it thinks it |
| // should result in some text |
| - NSText *edit = [[theEvent window] fieldEditor:YES forObject:nil]; |
| - in_key_event = true; |
| - [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
| - in_key_event = false; |
| + |
| + // Don't send cmd-<key> to interpretKeyEvents because it beeps. |
| + if (!(Fl::e_state & FL_META)) { |
| + NSText *edit = [[theEvent window] fieldEditor:YES forObject:nil]; |
| + in_key_event = true; |
| + [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
| + in_key_event = false; |
| + } |
| } |
| //NSLog(@"to text=%@ l=%d", [NSString stringWithUTF8String:Fl::e_text], Fl::e_length); |
| int handled = Fl::handle(FL_KEYDOWN, window); |
| @@ -1937,21 +2150,30 @@ |
| //NSLog(@"insertText: received=%@",received); |
| |
| if (!in_key_event) fl_lock_function(); |
| + |
| + // Simple keyboard widgets do not want these side channel inputs. |
| + if (use_simple_keyboard) |
| + goto end; |
| + |
| [FLView prepareEtext:received]; |
| + |
| // We can get called outside of key events (e.g. from the character |
| - // palette). Transform such actions to FL_PASTE events. |
| + // palette). We need to fake our own key event at that point. |
| if (!in_key_event) { |
| Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| - Fl::handle(FL_PASTE, target); |
| + Fl::e_keysym = Fl::e_original_keysym = 0; |
| + Fl::handle(FL_KEYDOWN, target); |
| // for some reason, the window does not redraw until the next mouse move or button push |
| // sending a 'redraw()' or 'awake()' does not solve the issue! |
| Fl::flush(); |
| } |
| + |
| +end: |
| if (!in_key_event) fl_unlock_function(); |
| } |
| |
| - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection { |
| - NSString *received; |
| + NSString *received, *current, *aggregate; |
| if (newSelection.location == 0) { |
| [self unmarkText]; |
| return; |
| @@ -1962,11 +2184,47 @@ |
| received = (NSString*)aString; |
| } |
| //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length); |
| + |
| + fl_lock_function(); |
| + |
| + // Simple keyboard widgets generally do not want these side channel |
| + // inputs, but we have no other way of getting dead keys so we make |
| + // an exception in that case. |
| + if (use_simple_keyboard) { |
| + if (in_key_event && (Fl::e_length == 0)) { |
| + [FLView prepareEtext:received]; |
| + |
| + Fl::e_text = (char*)cocoaDead2FLTK(Fl::e_text); |
| + Fl::e_length = strlen(Fl::e_text); |
| + } |
| + goto end; |
| + } |
| + |
| // This code creates the OS X behaviour of seeing dead keys as things |
| // are being composed. |
| + // |
| + // Note: The concatenation thing is because of how OS X deals with |
| + // invalid sequences. At that point it will spit out one call |
| + // to insertText with the now aborted sequence, and one new |
| + // call to setMarkedText with the new sequence. Since we want |
| + // both to be visible, we need to concatenate. |
| next_compose_length = newSelection.location; |
| - [FLView prepareEtext:received]; |
| - //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", received, Fl::e_length, next_compose_length); |
| + current = [NSString stringWithUTF8String:Fl::e_text]; |
| + aggregate = [current stringByAppendingString:received]; |
| + |
| + [FLView prepareEtext:aggregate]; |
| + //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", aggregate, Fl::e_length, next_compose_length); |
| + |
| + // We can get called outside of key events (e.g. from the character |
| + // palette). We need to fake our own key event at that point. |
| + if (!in_key_event) { |
| + Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| + Fl::e_keysym = Fl::e_original_keysym = 0; |
| + Fl::handle(FL_KEYDOWN, target); |
| + } |
| + |
| +end: |
| + fl_unlock_function(); |
| } |
| |
| - (void)unmarkText { |