static bool MCConvertNativeFromUTF16(const uint16_t *p_chars, uint32_t p_char_count, uint8_t*& r_output, uint32_t& r_output_length) { uint8_t *t_output; uint32_t t_output_length; if (!MCMemoryAllocate(p_char_count, t_output)) return false; uint32_t t_index; t_index = 0; t_output_length = 0; while(t_index < p_char_count) { if (p_chars[t_index] < 128 && (t_index == p_char_count - 1 || p_chars[t_index + 1] < 128)) { t_output[t_output_length++] = (char)p_chars[t_index]; t_index += 1; } else { uint32_t t_start; t_start = t_index; uint32_t t_codepoint; t_codepoint = MCUnicodeCodepointAdvance((const uint2 *)p_chars, p_char_count, t_index); while(t_index < p_char_count) { uint4 t_old_index; t_old_index = t_index; t_codepoint = MCUnicodeCodepointAdvance((const uint2 *)p_chars, p_char_count, t_index); if (MCUnicodeCodepointIsBase(t_codepoint)) { t_index = t_old_index; break; } } uint8_t t_char; if (!MCUnicodeMapToNative(p_chars + t_start, t_index - t_start, t_char)) t_char = '?'; t_output[t_output_length++] = t_char; } } MCMemoryReallocate(t_output, t_output_length, t_output); r_output = t_output; r_output_length = t_output_length; return true; }
static bool MCConvertNativeFromISO8859_1(const uint8_t *p_input, uint32_t p_input_length, uint8_t*& r_output, uint32_t& r_output_length) { uint8_t *t_output; if (!MCMemoryAllocate(p_input_length, t_output)) return false; for(uint32_t i = 0; i < p_input_length; i++) { uint2 t_input_char; t_input_char = MCUnicodeMapFromNative_MacRoman(p_input[i]); if (!MCUnicodeMapToNative(&t_input_char, 1, t_output[i])) t_output[i] = '?'; } r_output = t_output; r_output_length = p_input_length; return true; }
// MW-2014-06-25: [[ Bug 12370 ]] Map an input keyCode and mapped codepoint to // engine keysym and char. The engine expects keyCode to be the mapped key // if an ascii character and the raw keycode if not. static void map_key_to_engine(MCPlatformKeyCode p_key_code, codepoint_t p_mapped_codepoint, codepoint_t p_unmapped_codepoint, MCPlatformKeyCode& r_key_code, MCStringRef &r_native_char) { if (p_mapped_codepoint <= 0xffff) { uint16_t t_unicode_char; char_t t_native_char; // MW-2014-08-05: [[ Bug 13042 ]] This was a mis-merge from an updated fix to bug 12747 // (the code previously was using unmapped codepoint). t_unicode_char = p_mapped_codepoint & 0xffff; if (MCUnicodeMapToNative(&t_unicode_char, 1, t_native_char)) { /* UNCHECKED */ MCStringCreateWithNativeChars(&t_native_char, 1, r_native_char); // MW-2014-06-25: [[ Bug 12370 ]] The engine expects keyCode to be the mapped key whenever // the mapped key is ASCII. If the mapped key is not ASCII then the keyCode reflects // the raw (US English) keycode. // SN-2014-12-08: [[ Bug 14067 ]] Avoid to use the native char instead of the key code // the numeric keypad keys. if (isascii(t_native_char) && (p_key_code < kMCPlatformKeyCodeKeypadSpace || p_key_code > kMCPlatformKeyCodeKeypadEqual)) r_key_code = t_native_char; else r_key_code = p_key_code; return; } // SN-2014-12-05: [[ Bug 14162 ]] We can have unicode chars being typed. // We keep the given keycode (the codepoint) as the key code in these conditions. else { /* UNCHECKED */ MCStringCreateWithChars(&t_unicode_char, 1, r_native_char); r_key_code = p_key_code; return; } } r_native_char = MCValueRetain(kMCEmptyString); r_key_code = p_key_code; }
static void MCEventQueueDispatchEvent(MCEvent *p_event) { MCEvent *t_event; t_event = p_event; MCObject *t_menu; t_menu = MCdispatcher -> getmenu(); switch(t_event -> type) { case kMCEventTypeNotify: t_event -> notify . callback(t_event -> notify . state, true); break; case kMCEventTypeWindowReshape: // IM-2014-02-14: [[ HiDPI ]] update view backing scale t_event -> window . stack -> view_setbackingscale(t_event->window.scale); t_event -> window . stack -> view_configure(true); break; case kMCEventTypeMouseFocus: if (t_event -> mouse . focus . inside) { if (MCmousestackptr != t_event -> mouse . stack) { MCmousestackptr = t_event -> mouse . stack; MCmousestackptr -> enter(); } if (t_menu == nil) MCmousestackptr -> mfocus(MCmousex, MCmousey); else t_menu -> mfocus(MCmousex, MCmousey); } else if (MCmousestackptr == t_event -> mouse . stack) { MCmousestackptr -> munfocus(); MCmousestackptr = nil; } break; case kMCEventTypeMousePress: if (MCmousestackptr == t_event -> mouse . stack || t_menu != nil) { if (t_event -> mouse . press . state == kMCMousePressStateDown) MCbuttonstate |= (1 << t_event -> mouse . press . button); else MCbuttonstate &= ~(1 << t_event -> mouse . press . button); if (t_event -> mouse . press . state == kMCMousePressStateDown) { if (t_event -> mouse . time - s_click_time < MCdoubletime && MCU_abs(MCclicklocx - MCmousex) < MCdoubledelta && MCU_abs(MCclicklocy - MCmousey) < MCdoubledelta) s_click_count += 1; else s_click_count = 0; } else s_click_time = t_event -> mouse . time; MCeventtime = t_event -> mouse . time; MCmodifierstate = t_event -> mouse . press . modifiers; MCclicklocx = MCmousex; MCclicklocy = MCmousey; MCclickstackptr = MCmousestackptr; MCObject *t_target; t_target = t_menu != nil ? t_menu : MCclickstackptr; if (t_event -> mouse . press . state == kMCMousePressStateDown) { tripleclick = s_click_count == 2; if (s_click_count != 1) t_target -> mdown(t_event -> mouse . press . button + 1); else t_target -> doubledown(t_event -> mouse . press . button + 1); } else if (t_event -> mouse . press . state == kMCMousePressStateUp) { if (s_click_count != 1) t_target -> mup(t_event -> mouse . press . button + 1); else t_target -> doubleup(t_event -> mouse . press . button + 1); } else { s_click_count = 0; tripleclick = False; // If the press was 'released' i.e. cancelled then we stop messages, mup then // dispatch a mouseRelease message ourselves. // FG-2013-10-09 [[ Bugfix 11208 ]] // CS_NO_MESSAGES only applies to the target and not the controls it contains // so the mouse up message (on mouseUp) sets sent when it isn't desired // Hopefully nobody depends on the old behaviour... //t_target -> setstate(True, CS_NO_MESSAGES); //t_target -> mup(t_event -> mouse . press . button + 1); //t_target -> setstate(False, CS_NO_MESSAGES); bool old_lock = MClockmessages; MClockmessages = true; t_target -> mup(t_event -> mouse . press . button + 1); MClockmessages = old_lock; t_target -> message_with_args(MCM_mouse_release, t_event -> mouse . press . button + 1); } } break; case kMCEventTypeMouseWheel: // Notice that we recompute mfocused twice - this is because calling the key handler // could invalidate mfocused in between. if (MCmousestackptr == t_event -> mouse . stack) { MCObject *mfocused; mfocused = MCmousestackptr->getcard()->getmfocused(); if (mfocused == NULL) mfocused = MCmousestackptr -> getcard(); if (mfocused == NULL) mfocused = MCmousestackptr; MCeventtime = t_event -> mouse . time; MCmodifierstate = t_event -> mouse . wheel . modifiers; if (t_event -> mouse . wheel . dv != 0) mfocused -> kdown("", t_event -> mouse . wheel . dv < 0 ? XK_WheelUp : XK_WheelDown); mfocused = MCmousestackptr->getcard()->getmfocused(); if (mfocused == NULL) mfocused = MCmousestackptr -> getcard(); if (mfocused == NULL) mfocused = MCmousestackptr; if (t_event -> mouse . wheel . dh != 0) mfocused -> kdown("", t_event -> mouse . wheel . dh < 0 ? XK_WheelLeft : XK_WheelRight); } break; case kMCEventTypeMousePosition: if (MCmousestackptr == t_event -> mouse . stack || t_menu != nil) { MCeventtime = t_event -> mouse . time; MCmodifierstate = t_event -> mouse . position . modifiers; MCObject *t_target; t_target = t_menu != nil ? t_menu : MCmousestackptr; // IM-2013-09-30: [[ FullscreenMode ]] Translate mouse location to stack coords MCPoint t_mouseloc; t_mouseloc = MCPointMake(t_event->mouse.position.x, t_event->mouse.position.y); // IM-2013-10-03: [[ FullscreenMode ]] Transform mouseloc based on the mousestack t_mouseloc = MCmousestackptr->view_viewtostackloc(t_mouseloc); MCmousex = t_mouseloc.x; MCmousey = t_mouseloc.y; t_target -> mfocus(t_mouseloc . x, t_mouseloc . y); } break; case kMCEventTypeKeyFocus: if (t_event -> key . focus . owner) t_event -> key . stack -> kfocus(); else t_event -> key . stack -> kunfocus(); break; case kMCEventTypeKeyPress: { MCObject *t_target; t_target = t_menu != nil ? t_menu : t_event -> key . stack; MCmodifierstate = t_event -> key . press . modifiers; // If 'char_code' is 0, then this key press has not generated a // character. if (t_event -> key . press . char_code == 0) { t_target -> kdown(MCnullstring, t_event -> key . press . key_code); t_target -> kup(MCnullstring, t_event -> key . press . key_code); break; } // Otherwise 'char_code' is the unicode codepoint, so first map to // UTF-16 (not done properly yet...) uint2 t_unichar; t_unichar = (uint2)t_event -> key . press . char_code; // If we successfully map to native, then we can dispatch as a normal kdown uint1 t_char; if (MCUnicodeMapToNative(&t_unichar, 1, t_char)) { char t_buffer[2]; t_buffer[0] = t_char; t_buffer[1] = '\0'; t_target -> kdown(t_buffer, t_event -> key . press . key_code); t_target -> kup(t_buffer, t_event -> key . press . key_code); break; } // Otherwise we dispatch in a unicode way... if (!t_target -> kdown(MCnullstring, t_event -> key . press . key_code)) if (MCactivefield != nil) { MCString t_unibuffer; t_unibuffer . set((char *)&t_unichar, 2); // MW-2012-02-13: [[ Block Unicode ]] Use the new 'finsert' method in // unicode mode. MCactivefield -> finsertnew(FT_IMEINSERT, t_unibuffer, LCH_UNICODE, true); } t_target -> kup(MCnullstring, t_event -> key . press . key_code); } break; case kMCEventTypeImeCompose: { if (!MCactivefield) break; if (t_event -> ime . compose . enabled) MCactivefield -> startcomposition(); else MCactivefield -> stopcomposition(True, False); MCactivefield -> setcompositioncursoroffset(t_event -> ime . compose . offset * 2); MCString t_unichars; t_unichars . set((const char *)t_event -> ime . compose . chars, t_event -> ime . compose . char_count * sizeof(uint16_t)); // MW-2012-02-13: [[ Block Unicode ]] Use the new 'finsert' method in // unicode mode. MCactivefield -> finsertnew(FT_IMEINSERT, t_unichars, LCH_UNICODE, true); if (t_event -> ime . compose . enabled) { MCRectangle r; MCactivefield -> getcompositionrect(r, -1); MCModeConfigureIme(MCactivefield -> getstack(), true, r . x, r . y + r . height); } } break; #ifdef _MOBILE case kMCEventTypeTouch: handle_touch(t_event -> touch . stack, t_event -> touch . phase, t_event -> touch . id, t_event -> touch . taps, t_event -> touch . x, t_event -> touch . y); break; case kMCEventTypeMotion: { MCNameRef t_message; const char *t_motion; switch(t_event -> motion . type) { case kMCEventMotionShakeBegan: t_motion = "shake"; t_message = MCM_motion_start; break; case kMCEventMotionShakeEnded: t_motion = "shake"; t_message = MCM_motion_end; break; case kMCEventMotionShakeCancelled: t_motion = "shake"; t_message = MCM_motion_release; break; } MCdefaultstackptr -> getcurcard() -> message_with_args(t_message, t_motion); } break; case kMCEventTypeAcceleration: { char t_value[64 * 4 + 4]; sprintf(t_value, "%.6f,%.6f,%.6f,%f", t_event -> acceleration . x, t_event -> acceleration . y, t_event -> acceleration . z, t_event -> acceleration . t); MCdefaultstackptr -> getcurcard() -> message_with_args(MCM_acceleration_changed, t_value); } break; case kMCEventTypeOrientation: MCdefaultstackptr -> getcurcard() -> message(MCM_orientation_changed); break; case kMCEventTypeLocation: MCdefaultstackptr -> getcurcard() -> message(t_event -> location . error == nil ? MCM_location_changed : MCM_location_error); break; case kMCEventTypeHeading: MCdefaultstackptr -> getcurcard() -> message(t_event -> location . error == nil ? MCM_heading_changed : MCM_heading_error); break; case kMCEventTypeCustom: t_event -> custom . event -> Dispatch(); break; #endif } }
void MCPlatformHandleTextInputInsertText(MCPlatformWindowRef p_window, unichar_t *p_chars, uindex_t p_char_count, MCRange p_replace_range, MCRange p_selection_range, bool p_mark) { if (!MCactivefield) return; // SN-2014-12-04: [[ Bug 14152 ]] Locking the screen here doesn't allow the screen to refresh after // text input, inside an MCWait loop // MCRedrawLockScreen(); int32_t t_r_si, t_r_ei; t_r_si = 0; t_r_ei = INT32_MAX; MCactivefield -> resolvechars(0, t_r_si, t_r_ei, p_replace_range . offset, p_replace_range . length); // SN-2014-09-15: [[ Bug 13423 ]] t_was_compositing now used further in the function bool t_was_compositing; t_was_compositing = false; if (!p_mark) { // MW-2014-08-05: [[ Bug 13098 ]] If we have been compositing, then don't synthesise a // keyDown / keyUp. int4 si, ei; if (MCactivefield -> getcompositionrange(si, ei)) { if (si < t_r_si) t_r_si -= MCMin(t_r_si - si, ei - si); if (si < t_r_ei) t_r_ei -= MCMin(t_r_ei - si, ei - si); MCactivefield -> stopcomposition(True, False); t_was_compositing = true; } else t_was_compositing = false; // If the char count is 1 and the replacement range matches the current selection, // the char is native and the requested selection is after the char, then synthesis a // keydown/up pair. // MW-2014-06-25: [[ Bug 12370 ]] If the char is ascii then map appropriately so we get // the keycodes the engine expects. char_t t_char; if (!t_was_compositing && p_char_count == 1 && MCUnicodeMapToNative(p_chars, 1, t_char) && p_selection_range . offset == p_replace_range . offset + 1 && p_selection_range . length == 0) { int32_t t_s_si, t_s_ei; MCactivefield -> selectedmark(False, t_s_si, t_s_ei, False); if (t_s_si == t_r_si && t_s_ei == t_r_ei) { // SN-2014-09-15: [[ Bug 13423 ]] Send the messages for all the characters typed while (s_pending_key_down != nil) { // MW-2014-04-15: [[ Bug 12086 ]] Pass the keycode from the last event that was // passed to the IME. MCAutoStringRef t_mapped_char; MCPlatformKeyCode t_mapped_key_code; map_key_to_engine(s_pending_key_down -> key_code, s_pending_key_down -> mapped_codepoint, s_pending_key_down -> unmapped_codepoint, t_mapped_key_code, &t_mapped_char); // SN-2014-11-03: [[ Bug 13832 ]] Enqueue the event, instead of firing it now (we are still in the NSApplication's keyDown). // PM-2015-05-15: [[ Bug 15372]] call MCKeyMessageAppend before wkdown to prevent a crash if 'wait with messages' is used (since s_pending_key_down might become nil after wkdown MCKeyMessageAppend(s_pending_key_up, s_pending_key_down -> key_code, s_pending_key_down -> mapped_codepoint, s_pending_key_down -> unmapped_codepoint); MCdispatcher -> wkdown(p_window, *t_mapped_char, t_mapped_key_code); MCKeyMessageNext(s_pending_key_down); } return; } } } else { if (p_char_count == 0) MCactivefield -> stopcomposition(True, False); else { int4 si, ei; if (MCactivefield -> getcompositionrange(si, ei)) { if (si < t_r_si) t_r_si -= MCMin(t_r_si - si, ei - si); if (si < t_r_ei) t_r_ei -= MCMin(t_r_ei - si, ei - si); MCactivefield -> stopcomposition(True, False); } } } // SN-2014-09-14: [[ Bug 13423 ]] MCPlatformHandleRawKeyDown gets the US mac layout key, without any modifier included. // We need to update the elements: // if the key pressed leads to an actual char: // this wrong key is replaced by this new 'combined' char // if the key pressed fails to generate a char: // this wrong key is replaced by the dead-key char // SN-2015-04-10: [[ Bug 14205 ]] When using the dictation, there is no // pending key down, but the composition was still on though. // SN-2015-06-23: [[ Bug 3537 ]] We should not cast p_char as a uint1 if it // is not a native char. uint1 t_char[2]; bool t_is_native_char; t_is_native_char = MCUnicodeMapToNative(p_chars, 1, t_char[0]); t_char[1] = 0; if (t_was_compositing && s_pending_key_down && t_is_native_char) { s_pending_key_down -> key_code = (uint1)*t_char; s_pending_key_down -> mapped_codepoint = (uint1)*t_char; s_pending_key_down -> unmapped_codepoint = (uint1)*t_char; // SN-2015-05-18: [[ Bug 15385 ]] Enqueue the first char in the sequence // here - that will be the same as keyDown. // SN-2015-06-23: [[ Bug 3537 ]] In this only case, we don't want this // nativised char to be mapped again in MCPlatformHandleKeyUp. MCKeyMessageAppend(s_pending_key_up, (uint1)*t_char, (uint1)*t_char, (uint1)*t_char, false); } // Set the text. MCactivefield -> seltext(t_r_si, t_r_ei, False); if (p_mark) MCactivefield -> startcomposition(); // SN-2014-09-15: [[ Bug 13423 ]] If the character typed is not Unicode and follows a dead key character, then we send // [Raw]KeyDown/Up and remove the first character from the sequence of keys typed. // If the character successfully combined with the dead char before it in a native char, we don't use finsert // Otherwise, we have the dead char in p_chars, we need to remove the one stored first in the sequence MCAutoStringRef t_string; // SN-2015-01-20: [[ Bug 14406 ]] If we have a series of pending keys, we have two possibilities: // - typing IME characters: the characters are native, so we use the finsertnew // - typing dead characters: the character, if we arrive here, is > 127 // SN-2015-04-13: [[ Bug 14205 ]] Ensure that s_pending_key_down is not nil if (*p_chars > 127 && s_pending_key_down && s_pending_key_down -> next && t_is_native_char) { MCStringCreateWithNativeChars((const char_t *)t_char, 1, &t_string); MCdispatcher -> wkdown(p_window, *t_string, *t_char); MCKeyMessageNext(s_pending_key_down); } else { MCStringCreateWithChars(p_chars, p_char_count, &t_string); // SN-2014-12-05: [[ Bug 14162 ]] In case the character is a Unicode alphanumeric char, // then that's not a combining char - and it deserves its (raw)Key(Down|Up) messages uint32_t t_codepoint; t_codepoint = MCStringGetCodepointAtIndex(*t_string, 0); // SN-2015-05-18: [[ Bug 3537 ]] Use p_mark to determine whether we are // in an IME state // SN-2015-05-05: [[ Bug 15305 ]] Check that s_pending_key_down is not // nil before trying to use it, and use IME only if p_mark says so. if (s_pending_key_down && !p_mark) { MCAutoStringRef t_mapped_char; MCPlatformKeyCode t_mapped_key_code; map_key_to_engine(s_pending_key_down -> key_code, s_pending_key_down -> mapped_codepoint, s_pending_key_down -> unmapped_codepoint, t_mapped_key_code, &t_mapped_char); MCdispatcher -> wkdown(p_window, *t_string, *p_chars); // SN-2015-05-18: [[ Bug 3537 ]] If we were compositing, then we want // to send the same message for keyUp and keyDown - which might be // seeveral character-long if (t_was_compositing) MCdispatcher -> wkup(p_window, *t_string, *p_chars); else MCKeyMessageAppend(s_pending_key_up, *p_chars, s_pending_key_down -> mapped_codepoint, s_pending_key_down -> unmapped_codepoint); MCKeyMessageNext(s_pending_key_down); } else MCactivefield -> finsertnew(FT_IMEINSERT, *t_string, True); } // And update the selection range. int32_t t_s_si, t_s_ei; t_s_si = 0; t_s_ei = INT32_MAX; MCactivefield -> resolvechars(0, t_s_si, t_s_ei, p_selection_range . offset, p_selection_range . length); MCactivefield -> setcompositioncursoroffset(t_s_si - t_r_si); MCactivefield -> seltext(t_s_si, t_s_ei, True); // SN-2014-12-04: [[ Bug 14152 ]] Locking the screen here doesn't allow the screen to refresh after // text input, inside an MCWait loop // MCRedrawUnlockScreen(); }