Commit 192b8dbf authored by Ken Thomases's avatar Ken Thomases Committed by Alexandre Julliard

winemac: Implement an IME that uses the OS X input methods.

Developed with Aric Stewart.
parent 72be2327
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 \
......
......@@ -52,6 +52,8 @@ enum {
CGEventSourceKeyboardType keyboardType;
NSEvent* lastFlagsChanged;
BOOL inputSourceIsInputMethod;
BOOL inputSourceIsInputMethodValid;
CGFloat primaryScreenHeight;
BOOL primaryScreenHeightValid;
......
......@@ -81,6 +81,7 @@ int macdrv_err_on;
@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 @@ int macdrv_err_on;
@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 @@ int macdrv_err_on;
{
TISInputSourceRef inputSource;
inputSourceIsInputMethodValid = FALSE;
inputSource = TISCopyCurrentKeyboardLayoutInputSource();
if (inputSource)
{
......@@ -1227,6 +1243,25 @@ int macdrv_err_on;
[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;
}
......@@ -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;
......
......@@ -54,6 +54,9 @@
NSTimer* liveResizeDisplayTimer;
void* imeData;
BOOL commandDone;
BOOL causing_becomeKeyWindow;
BOOL ignore_windowMiniaturize;
BOOL ignore_windowDeminiaturize;
......
......@@ -118,10 +118,13 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
}
@interface WineContentView : NSView
@interface WineContentView : NSView <NSTextInputClient>
{
NSMutableArray* glContexts;
NSMutableArray* pendingGlContexts;
NSMutableAttributedString* markedText;
NSRange markedTextSelection;
}
- (void) addGLContext:(WineOpenGLContext*)context;
......@@ -154,6 +157,9 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
@property (readwrite, nonatomic) NSInteger levelWhenActive;
@property (assign, nonatomic) void* imeData;
@property (nonatomic) BOOL commandDone;
@end
......@@ -161,6 +167,7 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
- (void) dealloc
{
[markedText release];
[glContexts release];
[pendingGlContexts release];
[super dealloc];
......@@ -289,6 +296,147 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
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 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
@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 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
[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;
}
......@@ -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);
......
/*
* 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 <stdarg.h>
#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:<Unknown 0x%lx>\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);
}
......@@ -889,6 +889,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.@)
*/
HKL CDECL macdrv_ActivateKeyboardLayout(HKL hkl, UINT flags)
......
......@@ -64,6 +64,8 @@ static inline const char *wine_dbgstr_cgrect(CGRect cgrect)
CGRectGetMaxX(cgrect), CGRectGetMaxY(cgrect));
}
extern const char* debugstr_cf(CFTypeRef t) DECLSPEC_HIDDEN;
/**************************************************************************
* Mac GDI driver
......@@ -188,4 +190,17 @@ extern CFArrayRef create_app_icon_images(void) DECLSPEC_HIDDEN;
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 */
......@@ -135,6 +135,7 @@ extern void macdrv_window_rejected_focus(const struct macdrv_event *event) DECLS
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 @@ enum {
APP_DEACTIVATED,
APP_QUIT_REQUESTED,
DISPLAYS_CHANGED,
IM_SET_CURSOR_POS,
IM_SET_TEXT,
KEY_PRESS,
KEY_RELEASE,
KEYBOARD_CHANGED,
......@@ -196,6 +199,15 @@ typedef struct macdrv_event {
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;
unsigned long time_ms;
......@@ -348,6 +360,8 @@ extern void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGR
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 */
......
......@@ -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:
......
......@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment