diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index f351f331866..d576b81f617 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -2446,3 +2446,52 @@ void macdrv_set_mouse_capture_window(macdrv_window window) [[WineApplicationController sharedController] setMouseCaptureWindow:w]; }); } + +const CFStringRef macdrv_input_source_input_key = CFSTR("input"); +const CFStringRef macdrv_input_source_type_key = CFSTR("type"); +const CFStringRef macdrv_input_source_lang_key = CFSTR("lang"); + +/*********************************************************************** + * macdrv_create_input_source_list + */ +CFArrayRef macdrv_create_input_source_list(void) +{ + CFMutableArrayRef ret = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + OnMainThread(^{ + CFArrayRef input_list; + CFDictionaryRef filter_dict; + const void *filter_keys[2] = { kTISPropertyInputSourceCategory, kTISPropertyInputSourceIsSelectCapable }; + const void *filter_values[2] = { kTISCategoryKeyboardInputSource, kCFBooleanTrue }; + int i; + + filter_dict = CFDictionaryCreate(NULL, filter_keys, filter_values, sizeof(filter_keys)/sizeof(filter_keys[0]), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + input_list = TISCreateInputSourceList(filter_dict, false); + + for (i = 0; i < CFArrayGetCount(input_list); i++) + { + TISInputSourceRef input = (TISInputSourceRef)CFArrayGetValueAtIndex(input_list, i); + CFArrayRef source_langs = TISGetInputSourceProperty(input, kTISPropertyInputSourceLanguages); + CFDictionaryRef entry; + const void *input_keys[3] = { macdrv_input_source_input_key, + macdrv_input_source_type_key, + macdrv_input_source_lang_key }; + const void *input_values[3]; + + input_values[0] = input; + input_values[1] = TISGetInputSourceProperty(input, kTISPropertyInputSourceType); + input_values[2] = CFArrayGetValueAtIndex(source_langs, 0); + + entry = CFDictionaryCreate(NULL, input_keys, input_values, sizeof(input_keys) / sizeof(input_keys[0]), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFArrayAppendValue(ret, entry); + CFRelease(entry); + } + CFRelease(input_list); + CFRelease(filter_dict); + }); + + return ret; +} diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 1fffa228859..09eff9558a4 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -176,6 +176,7 @@ enum { kVK_UpArrow = 0x7E, }; +extern const CFStringRef kTISTypeKeyboardLayout; /* Indexed by Mac virtual keycode values defined above. */ static const struct { @@ -428,6 +429,90 @@ static int strip_apple_private_chars(LPWSTR bufW, int len) return len; } +static struct list layout_list = LIST_INIT( layout_list ); +struct layout +{ + struct list entry; + HKL hkl; + TISInputSourceRef input_source; +}; + +static CRITICAL_SECTION layout_list_section; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &layout_list_section, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": layout_list_section") } +}; +static CRITICAL_SECTION layout_list_section = { &critsect_debug, -1, 0, 0, 0, 0 }; + +static DWORD get_lcid(CFStringRef lang) +{ + CFRange range; + WCHAR str[10]; + + range.location = 0; + range.length = min(CFStringGetLength(lang), sizeof(str) / sizeof(str[0]) - 1); + CFStringGetCharacters(lang, range, str); + str[range.length] = 0; + return LocaleNameToLCID(str, 0); +} + +static HKL get_hkl(CFStringRef lang, CFStringRef type) +{ + ULONG_PTR lcid = get_lcid(lang); + struct layout *layout; + + /* Look for the last occurrence of this lcid in the list and if + present use that value + 0x10000 */ + LIST_FOR_EACH_ENTRY_REV(layout, &layout_list, struct layout, entry) + { + ULONG_PTR hkl = HandleToUlong(layout->hkl); + + if (LOWORD(hkl) == lcid) + { + lcid = (hkl & ~0xe0000000) + 0x10000; + break; + } + } + + if (!CFEqual(type, kTISTypeKeyboardLayout)) lcid |= 0xe0000000; + + return (HKL)lcid; +} + +/*********************************************************************** + * update_layout_list + * + * Must be called while holding the layout_list_section + */ +static void update_layout_list(void) +{ + CFArrayRef sources; + struct layout *layout; + int i; + + if (!list_empty(&layout_list)) return; + + sources = macdrv_create_input_source_list(); + + for (i = 0; i < CFArrayGetCount(sources); i++) + { + CFDictionaryRef dict = CFArrayGetValueAtIndex(sources, i); + TISInputSourceRef input = (TISInputSourceRef)CFDictionaryGetValue(dict, macdrv_input_source_input_key); + CFStringRef type = CFDictionaryGetValue(dict, macdrv_input_source_type_key); + CFStringRef lang = CFDictionaryGetValue(dict, macdrv_input_source_lang_key); + + layout = HeapAlloc(GetProcessHeap(), 0, sizeof(*layout)); + layout->input_source = (TISInputSourceRef)CFRetain(input); + layout->hkl = get_hkl(lang, type); + + list_add_tail(&layout_list, &layout->entry); + TRACE("adding new layout %p\n", layout->hkl); + } + + CFRelease(sources); +} /*********************************************************************** * macdrv_compute_keyboard_layout @@ -1221,6 +1306,36 @@ HKL CDECL macdrv_GetKeyboardLayout(DWORD thread_id) } +/*********************************************************************** + * GetKeyboardLayoutList (MACDRV.@) + */ +UINT CDECL macdrv_GetKeyboardLayoutList(INT size, HKL *list) +{ + int count = 0; + struct layout *layout; + + TRACE("%d, %p\n", size, list); + + EnterCriticalSection(&layout_list_section); + + update_layout_list(); + + LIST_FOR_EACH_ENTRY(layout, &layout_list, struct layout, entry) + { + if (list) + { + if (count >= size) break; + list[count] = layout->hkl; + TRACE("\t%d: %p\n", count, list[count]); + } + count++; + } + LeaveCriticalSection(&layout_list_section); + + TRACE("returning %d\n", count); + return count; +} + /*********************************************************************** * GetKeyboardLayoutName (MACDRV.@) */ diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index b452461f413..3ce1c26e6e6 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -123,6 +123,7 @@ MACDRV_HOTKEY_FAILURE, }; +typedef struct __TISInputSource *TISInputSourceRef; typedef struct macdrv_opaque_window* macdrv_window; typedef struct macdrv_opaque_event_queue* macdrv_event_queue; @@ -411,6 +412,10 @@ extern int macdrv_send_text_input_event(int pressed, unsigned int flags, int rep /* keyboard */ extern CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso) DECLSPEC_HIDDEN; +extern CFArrayRef macdrv_create_input_source_list(void) DECLSPEC_HIDDEN; +extern const CFStringRef macdrv_input_source_input_key DECLSPEC_HIDDEN; +extern const CFStringRef macdrv_input_source_type_key DECLSPEC_HIDDEN; +extern const CFStringRef macdrv_input_source_lang_key DECLSPEC_HIDDEN; /* clipboard */ diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index f3739a8aa55..58a894bb3f4 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -22,6 +22,7 @@ @ cdecl GetClipboardData(long) macdrv_GetClipboardData @ cdecl GetCursorPos(ptr) macdrv_GetCursorPos @ cdecl GetKeyboardLayout(long) macdrv_GetKeyboardLayout +@ cdecl GetKeyboardLayoutList(long ptr) macdrv_GetKeyboardLayoutList @ cdecl GetKeyboardLayoutName(ptr) macdrv_GetKeyboardLayoutName @ cdecl GetKeyNameText(long ptr long) macdrv_GetKeyNameText @ cdecl GetMonitorInfo(long ptr) macdrv_GetMonitorInfo