diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 2932b942f2b..924aef600f1 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -1,6 +1,6 @@ MODULE = winemac.drv IMPORTS = uuid user32 gdi32 advapi32 -DELAYIMPORTS = ole32 shell32 +DELAYIMPORTS = ole32 shell32 imm32 EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit C_SRCS = \ @@ -10,6 +10,7 @@ C_SRCS = \ event.c \ gdi.c \ image.c \ + ime.c \ keyboard.c \ macdrv_main.c \ mouse.c \ diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 6bcf6c19519..60a239cdf9b 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -52,6 +52,8 @@ @interface WineApplicationController : NSObject CGEventSourceKeyboardType keyboardType; NSEvent* lastFlagsChanged; + BOOL inputSourceIsInputMethod; + BOOL inputSourceIsInputMethodValid; CGFloat primaryScreenHeight; BOOL primaryScreenHeightValid; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 204734c0bf1..4e38e3b24a5 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -81,6 +81,7 @@ @interface WineApplicationController () @property (copy, nonatomic) NSArray* cursorFrames; @property (retain, nonatomic) NSTimer* cursorTimer; @property (retain, nonatomic) NSImage* applicationIcon; +@property (readonly, nonatomic) BOOL inputSourceIsInputMethod; - (void) setupObservations; - (void) applicationDidBecomeActive:(NSNotification *)notification; @@ -96,6 +97,19 @@ @implementation WineApplicationController @synthesize orderedWineWindows, applicationIcon; @synthesize cursorFrames, cursorTimer; + + (void) initialize + { + if (self == [WineApplicationController class]) + { + NSDictionary* defaults = [NSDictionary dictionaryWithObjectsAndKeys: + @"", @"NSQuotedKeystrokeBinding", + @"", @"NSRepeatCountBinding", + [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", + nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + } + } + + (WineApplicationController*) sharedController { static WineApplicationController* sharedController; @@ -323,6 +337,8 @@ - (void) keyboardSelectionDidChange { TISInputSourceRef inputSource; + inputSourceIsInputMethodValid = FALSE; + inputSource = TISCopyCurrentKeyboardLayoutInputSource(); if (inputSource) { @@ -1227,6 +1243,25 @@ - (void) setupObservations [NSTextInputContext self]; } + - (BOOL) inputSourceIsInputMethod + { + if (!inputSourceIsInputMethodValid) + { + TISInputSourceRef inputSource = TISCopyCurrentKeyboardInputSource(); + if (inputSource) + { + CFStringRef type = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceType); + inputSourceIsInputMethod = !CFEqual(type, kTISTypeKeyboardLayout); + CFRelease(inputSource); + } + else + inputSourceIsInputMethod = FALSE; + inputSourceIsInputMethodValid = TRUE; + } + + return inputSourceIsInputMethod; + } + /* * ---------- NSApplicationDelegate methods ---------- @@ -1646,3 +1681,17 @@ void macdrv_quit_reply(int reply) [NSApp replyToApplicationShouldTerminate:reply]; }); } + +/*********************************************************************** + * macdrv_using_input_method + */ +int macdrv_using_input_method(void) +{ + __block BOOL ret; + + OnMainThread(^{ + ret = [[WineApplicationController sharedController] inputSourceIsInputMethod]; + }); + + return ret; +} diff --git a/dlls/winemac.drv/cocoa_event.m b/dlls/winemac.drv/cocoa_event.m index bd923cc0103..1cbd5fbe007 100644 --- a/dlls/winemac.drv/cocoa_event.m +++ b/dlls/winemac.drv/cocoa_event.m @@ -453,6 +453,10 @@ void macdrv_release_event(macdrv_event *event) switch (event->type) { + case IM_SET_TEXT: + if (event->im_set_text.text) + CFRelease(event->im_set_text.text); + break; case KEYBOARD_CHANGED: CFRelease(event->keyboard_changed.uchr); break; diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index c76f735d117..ed5f1f90f0a 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -54,6 +54,9 @@ @interface WineWindow : NSPanel NSTimer* liveResizeDisplayTimer; + void* imeData; + BOOL commandDone; + BOOL causing_becomeKeyWindow; BOOL ignore_windowMiniaturize; BOOL ignore_windowDeminiaturize; diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index ca576930f90..e89dee50040 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -118,10 +118,13 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) } -@interface WineContentView : NSView +@interface WineContentView : NSView { NSMutableArray* glContexts; NSMutableArray* pendingGlContexts; + + NSMutableAttributedString* markedText; + NSRange markedTextSelection; } - (void) addGLContext:(WineOpenGLContext*)context; @@ -154,6 +157,9 @@ @interface WineWindow () @property (readwrite, nonatomic) NSInteger levelWhenActive; +@property (assign, nonatomic) void* imeData; +@property (nonatomic) BOOL commandDone; + @end @@ -161,6 +167,7 @@ @implementation WineContentView - (void) dealloc { + [markedText release]; [glContexts release]; [pendingGlContexts release]; [super dealloc]; @@ -289,6 +296,147 @@ - (BOOL) preservesContentDuringLiveResize return YES; } + - (BOOL)acceptsFirstResponder + { + return [[self window] contentView] == self; + } + + - (void) completeText:(NSString*)text + { + macdrv_event* event; + WineWindow* window = (WineWindow*)[self window]; + + event = macdrv_create_event(IM_SET_TEXT, window); + event->im_set_text.data = [window imeData]; + event->im_set_text.text = (CFStringRef)[text copy]; + event->im_set_text.complete = TRUE; + + [[window queue] postEvent:event]; + + macdrv_release_event(event); + + [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])]; + markedTextSelection = NSMakeRange(0, 0); + [[self inputContext] discardMarkedText]; + } + + /* + * ---------- NSTextInputClient methods ---------- + */ + - (NSTextInputContext*) inputContext + { + if (!markedText) + markedText = [[NSMutableAttributedString alloc] init]; + return [super inputContext]; + } + + - (void) insertText:(id)string replacementRange:(NSRange)replacementRange + { + if ([string isKindOfClass:[NSAttributedString class]]) + string = [string string]; + + if ([string isKindOfClass:[NSString class]]) + [self completeText:string]; + } + + - (void) doCommandBySelector:(SEL)aSelector + { + [(WineWindow*)[self window] setCommandDone:TRUE]; + } + + - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange + { + if ([string isKindOfClass:[NSAttributedString class]]) + string = [string string]; + + if ([string isKindOfClass:[NSString class]]) + { + macdrv_event* event; + WineWindow* window = (WineWindow*)[self window]; + + if (replacementRange.location == NSNotFound) + replacementRange = NSMakeRange(0, [markedText length]); + + [markedText replaceCharactersInRange:replacementRange withString:string]; + markedTextSelection = selectedRange; + markedTextSelection.location += replacementRange.location; + + event = macdrv_create_event(IM_SET_TEXT, window); + event->im_set_text.data = [window imeData]; + event->im_set_text.text = (CFStringRef)[[markedText string] copy]; + event->im_set_text.complete = FALSE; + + [[window queue] postEvent:event]; + + macdrv_release_event(event); + + event = macdrv_create_event(IM_SET_CURSOR_POS, window); + event->im_set_cursor_pos.data = [window imeData]; + event->im_set_cursor_pos.pos = markedTextSelection.location; + + [[window queue] postEvent:event]; + + macdrv_release_event(event); + } + } + + - (void) unmarkText + { + [self completeText:nil]; + } + + - (NSRange) selectedRange + { + return markedTextSelection; + } + + - (NSRange) markedRange + { + NSRange range = NSMakeRange(0, [markedText length]); + if (!range.length) + range.location = NSNotFound; + return range; + } + + - (BOOL) hasMarkedText + { + return [markedText length] > 0; + } + + - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange + { + if (aRange.location >= [markedText length]) + return nil; + + aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length])); + if (actualRange) + *actualRange = aRange; + return [markedText attributedSubstringFromRange:aRange]; + } + + - (NSArray*) validAttributesForMarkedText + { + return [NSArray array]; + } + + - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange + { + aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length])); + if (actualRange) + *actualRange = aRange; + return NSMakeRect(100, 100, aRange.length ? 1 : 0, 12); + } + + - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint + { + return NSNotFound; + } + + - (NSInteger) windowLevel + { + return [[self window] level]; + } + @end @@ -300,6 +448,7 @@ @implementation WineWindow @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue; @synthesize usePerPixelAlpha; @synthesize levelWhenActive; + @synthesize imeData, commandDone; + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf windowFrame:(NSRect)window_frame @@ -356,6 +505,7 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w [contentView addTrackingArea:trackingArea]; [window setContentView:contentView]; + [window setInitialFirstResponder:contentView]; return window; } @@ -1844,3 +1994,51 @@ uint32_t macdrv_window_background_color(void) return result; } + +/*********************************************************************** + * macdrv_send_text_input_event + */ +int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data) +{ + __block BOOL ret; + + OnMainThread(^{ + WineWindow* window = (WineWindow*)[NSApp keyWindow]; + if (![window isKindOfClass:[WineWindow class]]) + { + window = (WineWindow*)[NSApp mainWindow]; + if (![window isKindOfClass:[WineWindow class]] && [[NSApp orderedWineWindows] count]) + { + window = [[NSApp orderedWineWindows] objectAtIndex:0]; + if (![window isKindOfClass:[WineWindow class]]) + window = nil; + } + } + + if (window) + { + NSUInteger localFlags = flags; + CGEventRef c; + NSEvent* event; + + window.imeData = data; + fix_device_modifiers_by_generic(&localFlags); + + // An NSEvent created with +keyEventWithType:... is internally marked + // as synthetic and doesn't get sent through input methods. But one + // created from a CGEvent doesn't have that problem. + c = CGEventCreateKeyboardEvent(NULL, keyc, pressed); + CGEventSetFlags(c, localFlags); + CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat); + event = [NSEvent eventWithCGEvent:c]; + CFRelease(c); + + window.commandDone = FALSE; + ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone; + } + else + ret = FALSE; + }); + + return ret; +} diff --git a/dlls/winemac.drv/event.c b/dlls/winemac.drv/event.c index e1b2d7e1bda..cb305d64a16 100644 --- a/dlls/winemac.drv/event.c +++ b/dlls/winemac.drv/event.c @@ -35,6 +35,8 @@ static const char *dbgstr_event(int type) "APP_DEACTIVATED", "APP_QUIT_REQUESTED", "DISPLAYS_CHANGED", + "IM_SET_CURSOR_POS", + "IM_SET_TEXT", "KEY_PRESS", "KEY_RELEASE", "KEYBOARD_CHANGED", @@ -90,6 +92,8 @@ static macdrv_event_mask get_event_mask(DWORD mask) event_mask |= event_mask_for_type(APP_DEACTIVATED); event_mask |= event_mask_for_type(APP_QUIT_REQUESTED); event_mask |= event_mask_for_type(DISPLAYS_CHANGED); + event_mask |= event_mask_for_type(IM_SET_CURSOR_POS); + event_mask |= event_mask_for_type(IM_SET_TEXT); event_mask |= event_mask_for_type(STATUS_ITEM_CLICKED); event_mask |= event_mask_for_type(WINDOW_CLOSE_REQUESTED); event_mask |= event_mask_for_type(WINDOW_DID_MINIMIZE); @@ -173,6 +177,12 @@ void macdrv_handle_event(const macdrv_event *event) case DISPLAYS_CHANGED: macdrv_displays_changed(event); break; + case IM_SET_CURSOR_POS: + macdrv_im_set_cursor_pos(event); + break; + case IM_SET_TEXT: + macdrv_im_set_text(event); + break; case KEY_PRESS: case KEY_RELEASE: macdrv_key_event(hwnd, event); diff --git a/dlls/winemac.drv/ime.c b/dlls/winemac.drv/ime.c new file mode 100644 index 00000000000..e9f13693fce --- /dev/null +++ b/dlls/winemac.drv/ime.c @@ -0,0 +1,1511 @@ +/* + * The IME for interfacing with Mac input methods + * + * Copyright 2008, 2013 CodeWeavers, Aric Stewart + * Copyright 2013 Ken Thomases for CodeWeavers Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * Notes: + * The normal flow for IMM/IME Processing is as follows. + * 1) The Keyboard Driver generates key messages which are first passed to + * the IMM and then to IME via ImeProcessKey. If the IME returns 0 then + * it does not want the key and the keyboard driver then generates the + * WM_KEYUP/WM_KEYDOWN messages. However if the IME is going to process the + * key it returns non-zero. + * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to + * process the key. the IME modifies the HIMC structure to reflect the + * current state and generates any messages it needs the IMM to process. + * 3) IMM checks the messages and send them to the application in question. From + * here the IMM level deals with if the application is IME aware or not. + */ + +#include "config.h" + +#include + +#include "macdrv.h" +#include "winuser.h" +#include "imm.h" +#include "ddk/imm.h" + +WINE_DEFAULT_DEBUG_CHANNEL(imm); + +#define FROM_MACDRV ((HIMC)0xcafe1337) + +typedef struct _IMEPRIVATE { + BOOL bInComposition; + BOOL bInternalState; + HFONT textfont; + HWND hwndDefault; + + UINT repeat; +} IMEPRIVATE, *LPIMEPRIVATE; + +typedef struct _tagTRANSMSG { + UINT message; + WPARAM wParam; + LPARAM lParam; +} TRANSMSG, *LPTRANSMSG; + +static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','I','M','E',0}; + +static HIMC *hSelectedFrom = NULL; +static INT hSelectedCount = 0; + +/* MSIME messages */ +static UINT WM_MSIME_SERVICE; +static UINT WM_MSIME_RECONVERTOPTIONS; +static UINT WM_MSIME_MOUSE; +static UINT WM_MSIME_RECONVERTREQUEST; +static UINT WM_MSIME_RECONVERT; +static UINT WM_MSIME_QUERYPOSITION; +static UINT WM_MSIME_DOCUMENTFEED; + + +static HIMC RealIMC(HIMC hIMC) +{ + if (hIMC == FROM_MACDRV) + { + INT i; + HWND wnd = GetFocus(); + HIMC winHimc = ImmGetContext(wnd); + for (i = 0; i < hSelectedCount; i++) + if (winHimc == hSelectedFrom[i]) + return winHimc; + return NULL; + } + else + return hIMC; +} + +static LPINPUTCONTEXT LockRealIMC(HIMC hIMC) +{ + HIMC real_imc = RealIMC(hIMC); + if (real_imc) + return ImmLockIMC(real_imc); + else + return NULL; +} + +static BOOL UnlockRealIMC(HIMC hIMC) +{ + HIMC real_imc = RealIMC(hIMC); + if (real_imc) + return ImmUnlockIMC(real_imc); + else + return FALSE; +} + +static HIMCC ImeCreateBlankCompStr(void) +{ + HIMCC rc; + LPCOMPOSITIONSTRING ptr; + rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); + ptr = ImmLockIMCC(rc); + memset(ptr, 0, sizeof(COMPOSITIONSTRING)); + ptr->dwSize = sizeof(COMPOSITIONSTRING); + ImmUnlockIMCC(rc); + return rc; +} + +static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, + LPBYTE target, LPBYTE source, DWORD* lenParam, + DWORD* offsetParam, BOOL wchars) +{ + if (origLen > 0 && origOffset > 0) + { + int truelen = origLen; + if (wchars) + truelen *= sizeof(WCHAR); + + memcpy(&target[currentOffset], &source[origOffset], truelen); + + *lenParam = origLen; + *offsetParam = currentOffset; + currentOffset += truelen; + } + return currentOffset; +} + +static HIMCC updateCompStr(HIMCC old, LPCWSTR compstr, DWORD len, DWORD *flags) +{ + /* we need to make sure the CompStr, CompClaus and CompAttr fields are all + * set and correct */ + int needed_size; + HIMCC rc; + LPBYTE newdata = NULL; + LPBYTE olddata = NULL; + LPCOMPOSITIONSTRING new_one; + LPCOMPOSITIONSTRING lpcs = NULL; + INT current_offset = 0; + + TRACE("%s, %i\n", debugstr_wn(compstr, len), len); + + if (old == NULL && compstr == NULL && len == 0) + return NULL; + + if (compstr == NULL && len != 0) + { + ERR("compstr is NULL however we have a len! Please report\n"); + len = 0; + } + + if (old != NULL) + { + olddata = ImmLockIMCC(old); + lpcs = (LPCOMPOSITIONSTRING)olddata; + } + + needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + + len + sizeof(DWORD) * 2; + + if (lpcs != NULL) + { + needed_size += lpcs->dwCompReadAttrLen; + needed_size += lpcs->dwCompReadClauseLen; + needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD); + needed_size += lpcs->dwResultReadClauseLen; + needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD); + needed_size += lpcs->dwResultClauseLen; + needed_size += lpcs->dwResultStrLen * sizeof(DWORD); + needed_size += lpcs->dwPrivateSize; + } + rc = ImmCreateIMCC(needed_size); + newdata = ImmLockIMCC(rc); + new_one = (LPCOMPOSITIONSTRING)newdata; + + new_one->dwSize = needed_size; + current_offset = sizeof(COMPOSITIONSTRING); + if (lpcs != NULL) + { + current_offset = updateField(lpcs->dwCompReadAttrLen, + lpcs->dwCompReadAttrOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadAttrLen, + &new_one->dwCompReadAttrOffset, FALSE); + + current_offset = updateField(lpcs->dwCompReadClauseLen, + lpcs->dwCompReadClauseOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadClauseLen, + &new_one->dwCompReadClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwCompReadStrLen, + lpcs->dwCompReadStrOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadStrLen, + &new_one->dwCompReadStrOffset, TRUE); + + /* new CompAttr, CompClause, CompStr, dwCursorPos */ + new_one->dwDeltaStart = 0; + new_one->dwCursorPos = lpcs->dwCursorPos; + + current_offset = updateField(lpcs->dwResultReadClauseLen, + lpcs->dwResultReadClauseOffset, + current_offset, newdata, olddata, + &new_one->dwResultReadClauseLen, + &new_one->dwResultReadClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwResultReadStrLen, + lpcs->dwResultReadStrOffset, + current_offset, newdata, olddata, + &new_one->dwResultReadStrLen, + &new_one->dwResultReadStrOffset, TRUE); + + current_offset = updateField(lpcs->dwResultClauseLen, + lpcs->dwResultClauseOffset, + current_offset, newdata, olddata, + &new_one->dwResultClauseLen, + &new_one->dwResultClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwResultStrLen, + lpcs->dwResultStrOffset, + current_offset, newdata, olddata, + &new_one->dwResultStrLen, + &new_one->dwResultStrOffset, TRUE); + + current_offset = updateField(lpcs->dwPrivateSize, + lpcs->dwPrivateOffset, + current_offset, newdata, olddata, + &new_one->dwPrivateSize, + &new_one->dwPrivateOffset, FALSE); + } + else + { + new_one->dwCursorPos = len; + *flags |= GCS_CURSORPOS; + } + + /* set new data */ + /* CompAttr */ + new_one->dwCompAttrLen = len; + if (len > 0) + { + new_one->dwCompAttrOffset = current_offset; + memset(&newdata[current_offset], ATTR_INPUT, len); + current_offset += len; + } + + /* CompClause */ + if (len > 0) + { + new_one->dwCompClauseLen = sizeof(DWORD) * 2; + new_one->dwCompClauseOffset = current_offset; + *(DWORD*)&newdata[current_offset] = 0; + current_offset += sizeof(DWORD); + *(DWORD*)&newdata[current_offset] = len; + current_offset += sizeof(DWORD); + } + + /* CompStr */ + new_one->dwCompStrLen = len; + if (len > 0) + { + new_one->dwCompStrOffset = current_offset; + memcpy(&newdata[current_offset], compstr, len * sizeof(WCHAR)); + } + + + ImmUnlockIMCC(rc); + if (lpcs) + ImmUnlockIMCC(old); + + return rc; +} + +static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len) +{ + /* we need to make sure the ResultStr and ResultClause fields are all + * set and correct */ + int needed_size; + HIMCC rc; + LPBYTE newdata = NULL; + LPBYTE olddata = NULL; + LPCOMPOSITIONSTRING new_one; + LPCOMPOSITIONSTRING lpcs = NULL; + INT current_offset = 0; + + TRACE("%s, %i\n", debugstr_wn(resultstr, len), len); + + if (old == NULL && resultstr == NULL && len == 0) + return NULL; + + if (resultstr == NULL && len != 0) + { + ERR("resultstr is NULL however we have a len! Please report\n"); + len = 0; + } + + if (old != NULL) + { + olddata = ImmLockIMCC(old); + lpcs = (LPCOMPOSITIONSTRING)olddata; + } + + needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + + sizeof(DWORD) * 2; + + if (lpcs != NULL) + { + needed_size += lpcs->dwCompReadAttrLen; + needed_size += lpcs->dwCompReadClauseLen; + needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD); + needed_size += lpcs->dwCompAttrLen; + needed_size += lpcs->dwCompClauseLen; + needed_size += lpcs->dwCompStrLen * sizeof(DWORD); + needed_size += lpcs->dwResultReadClauseLen; + needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD); + needed_size += lpcs->dwPrivateSize; + } + rc = ImmCreateIMCC(needed_size); + newdata = ImmLockIMCC(rc); + new_one = (LPCOMPOSITIONSTRING)newdata; + + new_one->dwSize = needed_size; + current_offset = sizeof(COMPOSITIONSTRING); + if (lpcs != NULL) + { + current_offset = updateField(lpcs->dwCompReadAttrLen, + lpcs->dwCompReadAttrOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadAttrLen, + &new_one->dwCompReadAttrOffset, FALSE); + + current_offset = updateField(lpcs->dwCompReadClauseLen, + lpcs->dwCompReadClauseOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadClauseLen, + &new_one->dwCompReadClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwCompReadStrLen, + lpcs->dwCompReadStrOffset, + current_offset, newdata, olddata, + &new_one->dwCompReadStrLen, + &new_one->dwCompReadStrOffset, TRUE); + + current_offset = updateField(lpcs->dwCompAttrLen, + lpcs->dwCompAttrOffset, + current_offset, newdata, olddata, + &new_one->dwCompAttrLen, + &new_one->dwCompAttrOffset, FALSE); + + current_offset = updateField(lpcs->dwCompClauseLen, + lpcs->dwCompClauseOffset, + current_offset, newdata, olddata, + &new_one->dwCompClauseLen, + &new_one->dwCompClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwCompStrLen, + lpcs->dwCompStrOffset, + current_offset, newdata, olddata, + &new_one->dwCompStrLen, + &new_one->dwCompStrOffset, TRUE); + + new_one->dwCursorPos = lpcs->dwCursorPos; + new_one->dwDeltaStart = 0; + + current_offset = updateField(lpcs->dwResultReadClauseLen, + lpcs->dwResultReadClauseOffset, + current_offset, newdata, olddata, + &new_one->dwResultReadClauseLen, + &new_one->dwResultReadClauseOffset, FALSE); + + current_offset = updateField(lpcs->dwResultReadStrLen, + lpcs->dwResultReadStrOffset, + current_offset, newdata, olddata, + &new_one->dwResultReadStrLen, + &new_one->dwResultReadStrOffset, TRUE); + + /* new ResultClause , ResultStr */ + + current_offset = updateField(lpcs->dwPrivateSize, + lpcs->dwPrivateOffset, + current_offset, newdata, olddata, + &new_one->dwPrivateSize, + &new_one->dwPrivateOffset, FALSE); + } + + /* set new data */ + /* ResultClause */ + if (len > 0) + { + new_one->dwResultClauseLen = sizeof(DWORD) * 2; + new_one->dwResultClauseOffset = current_offset; + *(DWORD*)&newdata[current_offset] = 0; + current_offset += sizeof(DWORD); + *(DWORD*)&newdata[current_offset] = len; + current_offset += sizeof(DWORD); + } + + /* ResultStr */ + new_one->dwResultStrLen = len; + if (len > 0) + { + new_one->dwResultStrOffset = current_offset; + memcpy(&newdata[current_offset], resultstr, len * sizeof(WCHAR)); + } + ImmUnlockIMCC(rc); + if (lpcs) + ImmUnlockIMCC(old); + + return rc; +} + +static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPINPUTCONTEXT lpIMC; + LPTRANSMSG lpTransMsg; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * sizeof(TRANSMSG)); + if (!lpIMC->hMsgBuf) + return; + + lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); + if (!lpTransMsg) + return; + + lpTransMsg += lpIMC->dwNumMsgBuf; + lpTransMsg->message = msg; + lpTransMsg->wParam = wParam; + lpTransMsg->lParam = lParam; + + ImmUnlockIMCC(lpIMC->hMsgBuf); + lpIMC->dwNumMsgBuf++; + + ImmGenerateMessage(RealIMC(hIMC)); + UnlockRealIMC(hIMC); +} + +static void GenerateIMECHARMessages(HIMC hIMC, LPWSTR String, DWORD length) +{ + LPINPUTCONTEXT lpIMC; + LPTRANSMSG lpTransMsg; + DWORD i; + + if (length <= 0) + return; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + length) * sizeof(TRANSMSG)); + if (!lpIMC->hMsgBuf) + return; + + lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); + if (!lpTransMsg) + return; + + lpTransMsg += lpIMC->dwNumMsgBuf; + for (i = 0; i < length; i++) + { + lpTransMsg->message = WM_IME_CHAR; + lpTransMsg->wParam = String[i]; + lpTransMsg->lParam = 1; + lpTransMsg++; + } + + ImmUnlockIMCC(lpIMC->hMsgBuf); + lpIMC->dwNumMsgBuf += length; + + ImmGenerateMessage(RealIMC(hIMC)); + UnlockRealIMC(hIMC); +} + +static BOOL GenerateMessageToTransKey(LPDWORD lpTransBuf, UINT *uNumTranMsgs, + UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPTRANSMSG ptr; + + if (*uNumTranMsgs + 1 >= (UINT)*lpTransBuf) + return FALSE; + + ptr = (LPTRANSMSG)(lpTransBuf + 1 + *uNumTranMsgs * 3); + ptr->message = msg; + ptr->wParam = wParam; + ptr->lParam = lParam; + (*uNumTranMsgs)++; + + return TRUE; +} + + +static BOOL IME_RemoveFromSelected(HIMC hIMC) +{ + int i; + for (i = 0; i < hSelectedCount; i++) + { + if (hSelectedFrom[i] == hIMC) + { + if (i < hSelectedCount - 1) + memmove(&hSelectedFrom[i], &hSelectedFrom[i + 1], (hSelectedCount - i - 1) * sizeof(HIMC)); + hSelectedCount--; + return TRUE; + } + } + return FALSE; +} + +static void IME_AddToSelected(HIMC hIMC) +{ + hSelectedCount++; + if (hSelectedFrom) + hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount * sizeof(HIMC)); + else + hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC)); + hSelectedFrom[hSelectedCount - 1] = hIMC; +} + +static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) +{ + LPCOMPOSITIONSTRING compstr; + LPINPUTCONTEXT lpIMC; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + if (lpIMC->hCompStr) + compstr = ImmLockIMCC(lpIMC->hCompStr); + else + compstr = NULL; + + if (compstr == NULL || compstr->dwCompStrLen == 0) + ShowWindow(hwnd, SW_HIDE); + else if (showable) + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + + RedrawWindow(hwnd, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE); + + if (compstr != NULL) + ImmUnlockIMCC(lpIMC->hCompStr); + + UnlockRealIMC(hIMC); +} + +BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, LPCWSTR lpszOption) +{ + TRACE("\n"); + lpIMEInfo->dwPrivateDataSize = sizeof(IMEPRIVATE); + lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; + lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE; + lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC; + lpIMEInfo->fdwUICaps = UI_CAP_2700; + /* Tell App we cannot accept ImeSetCompositionString calls */ + /* FIXME: Can we? */ + lpIMEInfo->fdwSCSCaps = 0; + lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; + + lstrcpyW(lpszUIClass, UI_CLASS_NAME); + + return TRUE; +} + +BOOL WINAPI ImeConfigure(HKL hKL, HWND hWnd, DWORD dwMode, LPVOID lpData) +{ + FIXME("(%p, %p, %d, %p): stub\n", hKL, hWnd, dwMode, lpData); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource, LPCANDIDATELIST lpCandList, + DWORD dwBufLen, UINT uFlag) + +{ + FIXME("(%p, %s, %p, %d, %d): stub\n", hIMC, debugstr_w(lpSource), lpCandList, + dwBufLen, uFlag); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +BOOL WINAPI ImeDestroy(UINT uForce) +{ + TRACE("\n"); + HeapFree(GetProcessHeap(), 0, hSelectedFrom); + hSelectedFrom = NULL; + hSelectedCount = 0; + return TRUE; +} + +LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData) +{ + TRACE("%x %p\n", uSubFunc, lpData); + return 0; +} + +BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, CONST LPBYTE lpbKeyState) +{ + LPINPUTCONTEXT lpIMC; + BOOL inIME; + + TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08lx lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState); + + switch (vKey) + { + case VK_SHIFT: + case VK_CONTROL: + case VK_CAPITAL: + case VK_MENU: + return FALSE; + } + + inIME = macdrv_using_input_method(); + lpIMC = LockRealIMC(hIMC); + if (lpIMC) + { + LPIMEPRIVATE myPrivate; + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + + if (inIME && !myPrivate->bInternalState) + ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE); + else if (!inIME && myPrivate->bInternalState) + { + ShowWindow(myPrivate->hwndDefault, SW_HIDE); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = ImeCreateBlankCompStr(); + ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); + } + + myPrivate->repeat = (lKeyData >> 30) & 0x1; + + myPrivate->bInternalState = inIME; + ImmUnlockIMCC(lpIMC->hPrivate); + } + UnlockRealIMC(hIMC); + + return inIME; +} + +BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) +{ + LPINPUTCONTEXT lpIMC; + TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE"); + + if (hIMC == FROM_MACDRV) + { + ERR("ImeSelect should never be called from Cocoa\n"); + return FALSE; + } + + if (!hIMC) + return TRUE; + + /* not selected */ + if (!fSelect) + return IME_RemoveFromSelected(hIMC); + + IME_AddToSelected(hIMC); + + /* Initialize our structures */ + lpIMC = LockRealIMC(hIMC); + if (lpIMC != NULL) + { + LPIMEPRIVATE myPrivate; + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + myPrivate->bInComposition = FALSE; + myPrivate->bInternalState = FALSE; + myPrivate->textfont = NULL; + myPrivate->hwndDefault = NULL; + myPrivate->repeat = 0; + ImmUnlockIMCC(lpIMC->hPrivate); + UnlockRealIMC(hIMC); + } + + return TRUE; +} + +BOOL WINAPI ImeSetActiveContext(HIMC hIMC, BOOL fFlag) +{ + FIXME("(%p, %x): stub\n", hIMC, fFlag); + return TRUE; +} + +UINT WINAPI ImeToAsciiEx(UINT uVKey, UINT uScanCode, CONST LPBYTE lpbKeyState, + LPDWORD lpdwTransKey, UINT fuState, HIMC hIMC) +{ + UINT vkey; + LPINPUTCONTEXT lpIMC; + LPIMEPRIVATE myPrivate; + HWND hwndDefault; + UINT repeat; + INT rc; + + TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); + + vkey = LOWORD(uVKey); + + if (vkey == VK_KANA || vkey == VK_KANJI || vkey == VK_MENU) + { + TRACE("Skipping metakey\n"); + return 0; + } + + lpIMC = LockRealIMC(hIMC); + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + if (!myPrivate->bInternalState) + { + ImmUnlockIMCC(lpIMC->hPrivate); + UnlockRealIMC(hIMC); + return 0; + } + + repeat = myPrivate->repeat; + hwndDefault = myPrivate->hwndDefault; + ImmUnlockIMCC(lpIMC->hPrivate); + UnlockRealIMC(hIMC); + + TRACE("Processing Mac 0x%04x\n", vkey); + rc = macdrv_process_text_input(uVKey, uScanCode, repeat, lpbKeyState, hIMC); + + if (!rc) + { + UINT msgs = 0; + UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN; + + /* KeyStroke not processed by the IME + * so we need to rebuild the KeyDown message and pass it on to WINE + */ + if (!GenerateMessageToTransKey(lpdwTransKey, &msgs, msg, vkey, MAKELONG(0x0001, uScanCode))) + GenerateIMEMessage(hIMC, msg, vkey, MAKELONG(0x0001, uScanCode)); + + return msgs; + } + else + UpdateDataInDefaultIMEWindow(hIMC, hwndDefault, FALSE); + return 0; +} + +BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) +{ + BOOL bRet = FALSE; + LPINPUTCONTEXT lpIMC; + + TRACE("%p %i %i %i\n", hIMC, dwAction, dwIndex, dwValue); + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return FALSE; + + switch (dwAction) + { + case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break; + case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break; + case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break; + case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break; + case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break; + case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break; + case NI_CONTEXTUPDATED: + switch (dwValue) + { + case IMC_SETCOMPOSITIONWINDOW: FIXME("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONWINDOW\n"); break; + case IMC_SETCONVERSIONMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETCONVERSIONMODE\n"); break; + case IMC_SETSENTENCEMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETSENTENCEMODE\n"); break; + case IMC_SETCANDIDATEPOS: FIXME("NI_CONTEXTUPDATED: IMC_SETCANDIDATEPOS\n"); break; + case IMC_SETCOMPOSITIONFONT: + { + LPIMEPRIVATE myPrivate; + TRACE("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONFONT\n"); + + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + if (myPrivate->textfont) + { + DeleteObject(myPrivate->textfont); + myPrivate->textfont = NULL; + } + myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W); + ImmUnlockIMCC(lpIMC->hPrivate); + } + break; + case IMC_SETOPENSTATUS: + { + LPIMEPRIVATE myPrivate; + TRACE("NI_CONTEXTUPDATED: IMC_SETOPENSTATUS\n"); + + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + if (lpIMC->fOpen != myPrivate->bInternalState && myPrivate->bInComposition) + { + if(lpIMC->fOpen == FALSE) + { + GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); + myPrivate->bInComposition = FALSE; + } + else + { + GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); + GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0); + } + } + myPrivate->bInternalState = lpIMC->fOpen; + bRet = TRUE; + } + break; + default: FIXME("NI_CONTEXTUPDATED: Unknown\n"); break; + } + break; + case NI_COMPOSITIONSTR: + switch (dwIndex) + { + case CPS_COMPLETE: + { + HIMCC newCompStr; + DWORD cplen = 0; + LPWSTR cpstr; + LPCOMPOSITIONSTRING cs = NULL; + LPBYTE cdata = NULL; + LPIMEPRIVATE myPrivate; + + TRACE("NI_COMPOSITIONSTR: CPS_COMPLETE\n"); + + /* clear existing result */ + newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); + + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + + if (lpIMC->hCompStr) + { + cdata = ImmLockIMCC(lpIMC->hCompStr); + cs = (LPCOMPOSITIONSTRING)cdata; + cplen = cs->dwCompStrLen; + cpstr = (LPWSTR)&cdata[cs->dwCompStrOffset]; + ImmUnlockIMCC(lpIMC->hCompStr); + } + if (cplen > 0) + { + WCHAR param = cpstr[0]; + DWORD flags = GCS_COMPSTR; + + newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + + GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, flags); + + GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, + GCS_RESULTSTR | GCS_RESULTCLAUSE); + } + + GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); + + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + myPrivate->bInComposition = FALSE; + ImmUnlockIMCC(lpIMC->hPrivate); + + bRet = TRUE; + } + break; + case CPS_CONVERT: FIXME("NI_COMPOSITIONSTR: CPS_CONVERT\n"); break; + case CPS_REVERT: FIXME("NI_COMPOSITIONSTR: CPS_REVERT\n"); break; + case CPS_CANCEL: + { + LPIMEPRIVATE myPrivate; + + TRACE("NI_COMPOSITIONSTR: CPS_CANCEL\n"); + + if (lpIMC->hCompStr) + ImmDestroyIMCC(lpIMC->hCompStr); + + lpIMC->hCompStr = ImeCreateBlankCompStr(); + + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + if (myPrivate->bInComposition) + { + GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); + myPrivate->bInComposition = FALSE; + } + ImmUnlockIMCC(lpIMC->hPrivate); + bRet = TRUE; + } + break; + default: FIXME("NI_COMPOSITIONSTR: Unknown\n"); break; + } + break; + default: FIXME("Unknown Message\n"); break; + } + + UnlockRealIMC(hIMC); + return bRet; +} + +BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszRegister) +{ + FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister)); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, LPCWSTR lpszUnregister) +{ + FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszUnregister)); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +} + +UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf) +{ + FIXME("(%d, %p): stub\n", nItem, lpStyleBuf); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, LPCWSTR lpszReading, + DWORD dwStyle, LPCWSTR lpszRegister, LPVOID lpData) +{ + FIXME("(%p, %s, %d, %s, %p): stub\n", lpfnEnumProc, debugstr_w(lpszReading), dwStyle, + debugstr_w(lpszRegister), lpData); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, + LPCVOID lpRead, DWORD dwReadLen) +{ + LPINPUTCONTEXT lpIMC; + DWORD flags = 0; + WCHAR wParam = 0; + LPIMEPRIVATE myPrivate; + + TRACE("(%p, %d, %p, %d, %p, %d):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); + + /* + * Explanation: + * this sets the composition string in the imm32.dll level + * of the composition buffer. + * TODO: set the Cocoa window's marked text string and tell text input context + */ + + if (lpRead && dwReadLen) + FIXME("Reading string unimplemented\n"); + + lpIMC = LockRealIMC(hIMC); + + if (lpIMC == NULL) + return FALSE; + + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + + if (dwIndex == SCS_SETSTR) + { + HIMCC newCompStr; + + if (!myPrivate->bInComposition) + { + GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); + myPrivate->bInComposition = TRUE; + } + + flags = GCS_COMPSTR; + + if (dwCompLen && lpComp) + { + newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR), &flags); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + + wParam = ((const WCHAR*)lpComp)[0]; + flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART; + } + else + { + newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + } + } + + GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags); + ImmUnlockIMCC(lpIMC->hPrivate); + UnlockRealIMC(hIMC); + + return TRUE; +} + +DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC, DWORD dwFlags, DWORD dwType, LPIMEMENUITEMINFOW lpImeParentMenu, + LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize) +{ + FIXME("(%p, %x %x %p %p %x): stub\n", hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +static void IME_SetCursorPos(void* hIMC, DWORD pos) +{ + LPINPUTCONTEXT lpIMC; + LPCOMPOSITIONSTRING compstr; + + if (!hSelectedFrom) + return; + + lpIMC = LockRealIMC(hIMC); + if (!lpIMC) + return; + + compstr = ImmLockIMCC(lpIMC->hCompStr); + if (!compstr) + { + UnlockRealIMC(hIMC); + return; + } + + compstr->dwCursorPos = pos; + ImmUnlockIMCC(lpIMC->hCompStr); + UnlockRealIMC(hIMC); + GenerateIMEMessage(FROM_MACDRV, WM_IME_COMPOSITION, pos, GCS_CURSORPOS); + return; +} + + +static void IME_SetCompositionString(void* hIMC, LPCVOID lpComp, DWORD dwCompLen) +{ + ImeSetCompositionString(hIMC, SCS_SETSTR, lpComp, dwCompLen, NULL, 0); +} + +static void IME_NotifyComplete(void* hIMC) +{ + NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); +} + +/***** + * Internal functions to help with IME window management + */ +static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) +{ + PAINTSTRUCT ps; + RECT rect; + HDC hdc; + LPCOMPOSITIONSTRING compstr; + LPBYTE compdata = NULL; + HMONITOR monitor; + MONITORINFO mon_info; + INT offX = 0, offY = 0; + LPINPUTCONTEXT lpIMC; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + hdc = BeginPaint(hwnd, &ps); + + GetClientRect(hwnd, &rect); + FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); + + compdata = ImmLockIMCC(lpIMC->hCompStr); + compstr = (LPCOMPOSITIONSTRING)compdata; + + if (compstr->dwCompStrLen && compstr->dwCompStrOffset) + { + SIZE size; + POINT pt; + HFONT oldfont = NULL; + LPWSTR CompString; + LPIMEPRIVATE myPrivate; + + CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset); + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + + if (myPrivate->textfont) + oldfont = SelectObject(hdc, myPrivate->textfont); + + ImmUnlockIMCC(lpIMC->hPrivate); + + GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size); + pt.x = size.cx; + pt.y = size.cy; + LPtoDP(hdc, &pt, 1); + + /* + * How this works based on tests on windows: + * CFS_POINT: then we start our window at the point and grow it as large + * as it needs to be for the string. + * CFS_RECT: we still use the ptCurrentPos as a starting point and our + * window is only as large as we need for the string, but we do not + * grow such that our window exceeds the given rect. Wrapping if + * needed and possible. If our ptCurrentPos is outside of our rect + * then no window is displayed. + * CFS_FORCE_POSITION: appears to behave just like CFS_POINT + * maybe because the default MSIME does not do any IME adjusting. + */ + if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT) + { + POINT cpt = lpIMC->cfCompForm.ptCurrentPos; + ClientToScreen(lpIMC->hWnd, &cpt); + rect.left = cpt.x; + rect.top = cpt.y; + rect.right = rect.left + pt.x; + rect.bottom = rect.top + pt.y; + monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY); + } + else /* CFS_DEFAULT */ + { + /* Windows places the default IME window in the bottom left */ + HWND target = lpIMC->hWnd; + if (!target) target = GetFocus(); + + GetWindowRect(target, &rect); + rect.top = rect.bottom; + rect.right = rect.left + pt.x + 20; + rect.bottom = rect.top + pt.y + 20; + offX=offY=10; + monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY); + } + + if (lpIMC->cfCompForm.dwStyle == CFS_RECT) + { + RECT client; + client =lpIMC->cfCompForm.rcArea; + MapWindowPoints(lpIMC->hWnd, 0, (POINT *)&client, 2); + IntersectRect(&rect, &rect, &client); + /* TODO: Wrap the input if needed */ + } + + if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT) + { + /* make sure we are on the desktop */ + mon_info.cbSize = sizeof(mon_info); + GetMonitorInfoW(monitor, &mon_info); + + if (rect.bottom > mon_info.rcWork.bottom) + { + int shift = rect.bottom - mon_info.rcWork.bottom; + rect.top -= shift; + rect.bottom -= shift; + } + if (rect.left < 0) + { + rect.right -= rect.left; + rect.left = 0; + } + if (rect.right > mon_info.rcWork.right) + { + int shift = rect.right - mon_info.rcWork.right; + rect.left -= shift; + rect.right -= shift; + } + } + + SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOACTIVATE); + + TextOutW(hdc, offX, offY, CompString, compstr->dwCompStrLen); + + if (oldfont) + SelectObject(hdc, oldfont); + } + + ImmUnlockIMCC(lpIMC->hCompStr); + + EndPaint(hwnd, &ps); + UnlockRealIMC(hIMC); +} + +static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) +{ + TRACE("IME message WM_IME_COMPOSITION 0x%lx\n", lParam); + if (lParam & GCS_RESULTSTR) + { + LPCOMPOSITIONSTRING compstr; + LPBYTE compdata; + LPWSTR ResultStr; + HIMCC newCompStr; + LPINPUTCONTEXT lpIMC; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + TRACE("Posting result as IME_CHAR\n"); + compdata = ImmLockIMCC(lpIMC->hCompStr); + compstr = (LPCOMPOSITIONSTRING)compdata; + ResultStr = (LPWSTR)(compdata + compstr->dwResultStrOffset); + GenerateIMECHARMessages(hIMC, ResultStr, compstr->dwResultStrLen); + ImmUnlockIMCC(lpIMC->hCompStr); + + /* clear the buffer */ + newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); + ImmDestroyIMCC(lpIMC->hCompStr); + lpIMC->hCompStr = newCompStr; + UnlockRealIMC(hIMC); + } + else + UpdateDataInDefaultIMEWindow(hIMC, hwnd, TRUE); +} + +static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd) +{ + LPINPUTCONTEXT lpIMC; + + lpIMC = LockRealIMC(hIMC); + if (lpIMC == NULL) + return; + + TRACE("IME message WM_IME_STARTCOMPOSITION\n"); + lpIMC->hWnd = GetFocus(); + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + UnlockRealIMC(hIMC); +} + +static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (wParam) + { + case IMN_OPENSTATUSWINDOW: + FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n"); + break; + case IMN_CLOSESTATUSWINDOW: + FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n"); + break; + case IMN_OPENCANDIDATE: + FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n"); + break; + case IMN_CHANGECANDIDATE: + FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n"); + break; + case IMN_CLOSECANDIDATE: + FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n"); + break; + case IMN_SETCONVERSIONMODE: + FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n"); + break; + case IMN_SETSENTENCEMODE: + FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n"); + break; + case IMN_SETOPENSTATUS: + FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n"); + break; + case IMN_SETCANDIDATEPOS: + FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n"); + break; + case IMN_SETCOMPOSITIONFONT: + FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n"); + break; + case IMN_SETCOMPOSITIONWINDOW: + FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n"); + break; + case IMN_GUIDELINE: + FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n"); + break; + case IMN_SETSTATUSWINDOWPOS: + FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n"); + break; + default: + FIXME("WM_IME_NOTIFY:\n", wParam); + break; + } + return 0; +} + +static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT rc = 0; + HIMC hIMC; + + TRACE("Incoming Message 0x%x (0x%08lx, 0x%08lx)\n", msg, wParam, lParam); + + /* + * Each UI window contains the current Input Context. + * This Input Context can be obtained by calling GetWindowLong + * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message. + * The UI window can refer to this Input Context and handles the + * messages. + */ + + hIMC = (HIMC)GetWindowLongPtrW(hwnd, IMMGWL_IMC); + if (!hIMC) + hIMC = RealIMC(FROM_MACDRV); + + /* if we have no hIMC there are many messages we cannot process */ + if (hIMC == NULL) + { + switch (msg) { + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_IME_NOTIFY: + case WM_IME_CONTROL: + case WM_IME_COMPOSITIONFULL: + case WM_IME_SELECT: + case WM_IME_CHAR: + return 0L; + default: + break; + } + } + + switch (msg) + { + case WM_CREATE: + { + LPIMEPRIVATE myPrivate; + LPINPUTCONTEXT lpIMC; + + SetWindowTextA(hwnd, "Wine Ime Active"); + + lpIMC = LockRealIMC(hIMC); + if (lpIMC) + { + myPrivate = ImmLockIMCC(lpIMC->hPrivate); + myPrivate->hwndDefault = hwnd; + ImmUnlockIMCC(lpIMC->hPrivate); + } + UnlockRealIMC(hIMC); + + return TRUE; + } + case WM_PAINT: + PaintDefaultIMEWnd(hIMC, hwnd); + return FALSE; + + case WM_NCCREATE: + return TRUE; + + case WM_SETFOCUS: + if (wParam) + SetFocus((HWND)wParam); + else + FIXME("Received focus, should never have focus\n"); + break; + case WM_IME_COMPOSITION: + DefaultIMEComposition(hIMC, hwnd, lParam); + break; + case WM_IME_STARTCOMPOSITION: + DefaultIMEStartComposition(hIMC, hwnd); + break; + case WM_IME_ENDCOMPOSITION: + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_ENDCOMPOSITION", wParam, lParam); + ShowWindow(hwnd, SW_HIDE); + break; + case WM_IME_SELECT: + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_SELECT", wParam, lParam); + break; + case WM_IME_CONTROL: + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_IME_CONTROL", wParam, lParam); + rc = 1; + break; + case WM_IME_NOTIFY: + rc = ImeHandleNotify(hIMC, hwnd, msg, wParam, lParam); + break; + default: + TRACE("Non-standard message 0x%x\n", msg); + } + /* check the MSIME messages */ + if (msg == WM_MSIME_SERVICE) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_SERVICE", wParam, lParam); + rc = FALSE; + } + else if (msg == WM_MSIME_RECONVERTOPTIONS) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERTOPTIONS", wParam, lParam); + } + else if (msg == WM_MSIME_MOUSE) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_MOUSE", wParam, lParam); + } + else if (msg == WM_MSIME_RECONVERTREQUEST) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERTREQUEST", wParam, lParam); + } + else if (msg == WM_MSIME_RECONVERT) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_RECONVERT", wParam, lParam); + } + else if (msg == WM_MSIME_QUERYPOSITION) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_QUERYPOSITION", wParam, lParam); + } + else if (msg == WM_MSIME_DOCUMENTFEED) + { + TRACE("IME message %s, 0x%lx, 0x%lx\n", "WM_MSIME_DOCUMENTFEED", wParam, lParam); + } + /* DefWndProc if not an IME message */ + if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || + (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) + rc = DefWindowProcW(hwnd, msg, wParam, lParam); + + return rc; +} + + +/* Interfaces to other parts of the Mac driver */ + +void IME_RegisterClasses(HINSTANCE hImeInst) +{ + WNDCLASSW wndClass; + ZeroMemory(&wndClass, sizeof(WNDCLASSW)); + wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 2 * sizeof(LONG_PTR); + wndClass.hInstance = hImeInst; + wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW); + wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION); + wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = UI_CLASS_NAME; + + RegisterClassW(&wndClass); + + WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); + WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions"); + WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); + WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); + WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); + WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); + WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); +} + + +/*********************************************************************** + * macdrv_im_set_cursor_pos + */ +void macdrv_im_set_cursor_pos(const macdrv_event *event) +{ + HWND hwnd = macdrv_get_window_hwnd(event->window); + void *himc = event->im_set_cursor_pos.data; + + TRACE("win %p/%p himc %p pos %u\n", hwnd, event->window, himc, event->im_set_cursor_pos.pos); + + if (!himc) himc = RealIMC(FROM_MACDRV); + + IME_SetCursorPos(himc, event->im_set_cursor_pos.pos); +} + + +/*********************************************************************** + * macdrv_im_set_text + */ +void macdrv_im_set_text(const macdrv_event *event) +{ + HWND hwnd = macdrv_get_window_hwnd(event->window); + void *himc = event->im_set_text.data; + + TRACE("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, himc, + debugstr_cf(event->im_set_text.text), event->im_set_text.complete); + + if (!himc) himc = RealIMC(FROM_MACDRV); + + if (event->im_set_text.text) + { + CFIndex length = CFStringGetLength(event->im_set_text.text); + const UniChar *chars = CFStringGetCharactersPtr(event->im_set_text.text); + UniChar *buffer = NULL; + + if (!chars) + { + buffer = HeapAlloc(GetProcessHeap(), 0, length * sizeof(*buffer)); + CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), buffer); + chars = buffer; + } + + if (himc) + IME_SetCompositionString(himc, chars, length * sizeof(*chars)); + else + { + INPUT input; + CFIndex i; + + input.type = INPUT_KEYBOARD; + input.ki.wVk = 0; + input.ki.time = 0; + input.ki.dwExtraInfo = 0; + + for (i = 0; i < length; i++) + { + input.ki.wScan = chars[i]; + input.ki.dwFlags = KEYEVENTF_UNICODE; + __wine_send_input(hwnd, &input); + + input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; + __wine_send_input(hwnd, &input); + } + } + + HeapFree(GetProcessHeap(), 0, buffer); + } + + if (event->im_set_text.complete) + IME_NotifyComplete(himc); +} diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 7b773e3347f..33f79944df2 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -888,6 +888,53 @@ static BOOL match_keyboard_layout(HKL hkl) } +/*********************************************************************** + * macdrv_process_text_input + */ +BOOL macdrv_process_text_input(UINT vkey, UINT scan, UINT repeat, const BYTE *key_state, void *himc) +{ + struct macdrv_thread_data *thread_data = macdrv_thread_data(); + unsigned int flags; + int keyc; + BOOL ret = FALSE; + + TRACE("vkey 0x%04x scan 0x%04x repeat %u himc %p\n", vkey, scan, repeat, himc); + + flags = thread_data->last_modifiers; + if (key_state[VK_SHIFT] & 0x80) + flags |= NX_SHIFTMASK; + else + flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK); + if (key_state[VK_CAPITAL] & 0x01) + flags |= NX_ALPHASHIFTMASK; + else + flags &= ~NX_ALPHASHIFTMASK; + if (key_state[VK_CONTROL] & 0x80) + flags |= NX_CONTROLMASK; + else + flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK); + if (key_state[VK_MENU] & 0x80) + flags |= NX_COMMANDMASK; + else + flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK); + + /* Find the Mac keycode corresponding to the scan code */ + for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]); keyc++) + if (thread_data->keyc2vkey[keyc] == vkey) break; + + if (keyc >= sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0])) + goto done; + + TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc); + + ret = macdrv_send_text_input_event(((scan & 0x8000) == 0), flags, repeat, keyc, himc); + +done: + TRACE(" -> %s\n", ret ? "TRUE" : "FALSE"); + return ret; +} + + /*********************************************************************** * ActivateKeyboardLayout (MACDRV.@) */ diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 97ff08b7772..bdbe8d177fc 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -64,6 +64,8 @@ static inline RECT rect_from_cgrect(CGRect cgrect) CGRectGetMaxX(cgrect), CGRectGetMaxY(cgrect)); } +extern const char* debugstr_cf(CFTypeRef t) DECLSPEC_HIDDEN; + /************************************************************************** * Mac GDI driver @@ -188,4 +190,17 @@ extern CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP extern void macdrv_status_item_clicked(const macdrv_event *event) DECLSPEC_HIDDEN; + +/************************************************************************** + * Mac IME driver + */ + +extern void IME_RegisterClasses(HINSTANCE hImeInst) DECLSPEC_HIDDEN; + +extern BOOL macdrv_process_text_input(UINT vkey, UINT scan, UINT repeat, const BYTE *key_state, + void *himc) DECLSPEC_HIDDEN; + +extern void macdrv_im_set_cursor_pos(const macdrv_event *event) DECLSPEC_HIDDEN; +extern void macdrv_im_set_text(const macdrv_event *event) DECLSPEC_HIDDEN; + #endif /* __WINE_MACDRV_H */ diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index c4819c31996..2c0ee9d2eb0 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -135,6 +135,7 @@ extern void macdrv_beep(void) DECLSPEC_HIDDEN; extern void macdrv_set_application_icon(CFArrayRef images) DECLSPEC_HIDDEN; extern void macdrv_quit_reply(int reply) DECLSPEC_HIDDEN; +extern int macdrv_using_input_method(void) DECLSPEC_HIDDEN; /* cursor */ @@ -156,6 +157,8 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, APP_DEACTIVATED, APP_QUIT_REQUESTED, DISPLAYS_CHANGED, + IM_SET_CURSOR_POS, + IM_SET_TEXT, KEY_PRESS, KEY_RELEASE, KEYBOARD_CHANGED, @@ -195,6 +198,15 @@ extern int macdrv_set_display_mode(const struct macdrv_display* display, struct { int activating; } displays_changed; + struct { + void *data; + unsigned int pos; + } im_set_cursor_pos; + struct { + void *data; + CFStringRef text; /* new text or NULL if just completing existing text */ + unsigned int complete; /* is completing text? */ + } im_set_text; struct { CGKeyCode keycode; CGEventFlags modifiers; @@ -348,6 +360,8 @@ extern void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat extern void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c) DECLSPEC_HIDDEN; extern void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c) DECLSPEC_HIDDEN; extern uint32_t macdrv_window_background_color(void) DECLSPEC_HIDDEN; +extern int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, + void* data) DECLSPEC_HIDDEN; /* keyboard */ diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 353b4ca7361..f4cb7bed31c 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -206,6 +206,7 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) switch(reason) { case DLL_PROCESS_ATTACH: + IME_RegisterClasses(hinst); ret = process_attach(); break; case DLL_THREAD_DETACH: diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 061acb3d27f..29659b3a946 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -49,3 +49,21 @@ # System tray @ cdecl wine_notify_icon(long ptr) + +# IME +@ stdcall ImeConfigure(long long long ptr) +@ stdcall ImeConversionList(long wstr ptr long long) +@ stdcall ImeDestroy(long) +@ stdcall ImeEnumRegisterWord(ptr wstr long wstr ptr) +@ stdcall ImeEscape(long long ptr) +@ stdcall ImeGetImeMenuItems(long long long ptr ptr long) +@ stdcall ImeGetRegisterWordStyle(wstr long wstr) +@ stdcall ImeInquire(ptr wstr wstr) +@ stdcall ImeProcessKey(long long long ptr) +@ stdcall ImeRegisterWord(wstr long wstr) +@ stdcall ImeSelect(long long) +@ stdcall ImeSetActiveContext(long long) +@ stdcall ImeSetCompositionString(long long ptr long ptr long) +@ stdcall ImeToAsciiEx(long long ptr ptr long long) +@ stdcall ImeUnregisterWord(wstr long wstr) +@ stdcall NotifyIME(long long long long)