winemac: Build a map from Mac virtual key codes to Win32 vkeys and scan codes based on Mac keyboard layout.

This commit is contained in:
Ken Thomases 2013-02-03 17:20:11 -06:00 committed by Alexandre Julliard
parent 8a0c8d9264
commit b78eee3172
7 changed files with 719 additions and 1 deletions

View File

@ -1,11 +1,12 @@
MODULE = winemac.drv
IMPORTS = user32 gdi32 advapi32
EXTRALIBS = -framework AppKit
EXTRALIBS = -framework AppKit -framework Carbon
C_SRCS = \
display.c \
event.c \
gdi.c \
keyboard.c \
macdrv_main.c \
mouse.c \
scroll.c \

View File

@ -40,8 +40,12 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
NSMutableArray* keyWindows;
NSMutableSet* triedWindows;
unsigned long windowFocusSerial;
CGEventSourceKeyboardType keyboardType;
}
@property (nonatomic) CGEventSourceKeyboardType keyboardType;
- (void) transformProcessToForeground;
- (BOOL) registerEventQueue:(WineEventQueue*)queue;

View File

@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import <Carbon/Carbon.h>
#import "cocoa_app.h"
#import "cocoa_event.h"
#import "cocoa_window.h"
@ -28,6 +30,8 @@
@implementation WineApplication
@synthesize keyboardType;
- (id) init
{
self = [super init];
@ -213,6 +217,8 @@ - (void)applicationWillFinishLaunching:(NSNotification *)notification
NSWindow* window = [note object];
[keyWindows removeObjectIdenticalTo:window];
}];
self.keyboardType = LMGetKbdType();
}
@end
@ -270,3 +276,31 @@ void macdrv_window_rejected_focus(const macdrv_event *event)
[NSApp windowRejectedFocusEvent:event];
});
}
/***********************************************************************
* macdrv_get_keyboard_layout
*
* Returns the keyboard layout uchr data.
*/
CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso)
{
__block CFDataRef result = NULL;
OnMainThread(^{
TISInputSourceRef inputSource;
inputSource = TISCopyCurrentKeyboardLayoutInputSource();
if (inputSource)
{
CFDataRef uchr = TISGetInputSourceProperty(inputSource,
kTISPropertyUnicodeKeyLayoutData);
result = CFDataCreateCopy(NULL, uchr);
CFRelease(inputSource);
*keyboard_type = ((WineApplication*)NSApp).keyboardType;
*is_iso = (KBGetLayoutType(*keyboard_type) == kKeyboardISO);
}
});
return result;
}

663
dlls/winemac.drv/keyboard.c Normal file
View File

@ -0,0 +1,663 @@
/*
* MACDRV keyboard driver
*
* Copyright 1993 Bob Amstadt
* Copyright 1996 Albrecht Kleine
* Copyright 1997 David Faure
* Copyright 1998 Morten Welinder
* Copyright 1998 Ulrich Weigand
* Copyright 1999 Ove Kåven
* Copyright 2011, 2012, 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
*/
#include "config.h"
#include "macdrv.h"
#include "winuser.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
/* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
enum {
cmdKeyBit = 8,
shiftKeyBit = 9,
alphaLockBit = 10,
optionKeyBit = 11,
controlKeyBit = 12,
};
enum {
cmdKey = 1 << cmdKeyBit,
shiftKey = 1 << shiftKeyBit,
alphaLock = 1 << alphaLockBit,
optionKey = 1 << optionKeyBit,
controlKey = 1 << controlKeyBit,
};
/* Mac virtual key code definitions from <Carbon/HIToolbox/Events.h>. */
enum {
kVK_ANSI_A = 0x00,
kVK_ANSI_S = 0x01,
kVK_ANSI_D = 0x02,
kVK_ANSI_F = 0x03,
kVK_ANSI_H = 0x04,
kVK_ANSI_G = 0x05,
kVK_ANSI_Z = 0x06,
kVK_ANSI_X = 0x07,
kVK_ANSI_C = 0x08,
kVK_ANSI_V = 0x09,
kVK_ISO_Section = 0x0A,
kVK_ANSI_B = 0x0B,
kVK_ANSI_Q = 0x0C,
kVK_ANSI_W = 0x0D,
kVK_ANSI_E = 0x0E,
kVK_ANSI_R = 0x0F,
kVK_ANSI_Y = 0x10,
kVK_ANSI_T = 0x11,
kVK_ANSI_1 = 0x12,
kVK_ANSI_2 = 0x13,
kVK_ANSI_3 = 0x14,
kVK_ANSI_4 = 0x15,
kVK_ANSI_6 = 0x16,
kVK_ANSI_5 = 0x17,
kVK_ANSI_Equal = 0x18,
kVK_ANSI_9 = 0x19,
kVK_ANSI_7 = 0x1A,
kVK_ANSI_Minus = 0x1B,
kVK_ANSI_8 = 0x1C,
kVK_ANSI_0 = 0x1D,
kVK_ANSI_RightBracket = 0x1E,
kVK_ANSI_O = 0x1F,
kVK_ANSI_U = 0x20,
kVK_ANSI_LeftBracket = 0x21,
kVK_ANSI_I = 0x22,
kVK_ANSI_P = 0x23,
kVK_Return = 0x24,
kVK_ANSI_L = 0x25,
kVK_ANSI_J = 0x26,
kVK_ANSI_Quote = 0x27,
kVK_ANSI_K = 0x28,
kVK_ANSI_Semicolon = 0x29,
kVK_ANSI_Backslash = 0x2A,
kVK_ANSI_Comma = 0x2B,
kVK_ANSI_Slash = 0x2C,
kVK_ANSI_N = 0x2D,
kVK_ANSI_M = 0x2E,
kVK_ANSI_Period = 0x2F,
kVK_Tab = 0x30,
kVK_Space = 0x31,
kVK_ANSI_Grave = 0x32,
kVK_Delete = 0x33,
kVK_Escape = 0x35,
kVK_RightCommand = 0x36, /* invented for Wine; co-opt unused key code */
kVK_Command = 0x37,
kVK_Shift = 0x38,
kVK_CapsLock = 0x39,
kVK_Option = 0x3A,
kVK_Control = 0x3B,
kVK_RightShift = 0x3C,
kVK_RightOption = 0x3D,
kVK_RightControl = 0x3E,
kVK_Function = 0x3F,
kVK_F17 = 0x40,
kVK_ANSI_KeypadDecimal = 0x41,
kVK_ANSI_KeypadMultiply = 0x43,
kVK_ANSI_KeypadPlus = 0x45,
kVK_ANSI_KeypadClear = 0x47,
kVK_VolumeUp = 0x48,
kVK_VolumeDown = 0x49,
kVK_Mute = 0x4A,
kVK_ANSI_KeypadDivide = 0x4B,
kVK_ANSI_KeypadEnter = 0x4C,
kVK_ANSI_KeypadMinus = 0x4E,
kVK_F18 = 0x4F,
kVK_F19 = 0x50,
kVK_ANSI_KeypadEquals = 0x51,
kVK_ANSI_Keypad0 = 0x52,
kVK_ANSI_Keypad1 = 0x53,
kVK_ANSI_Keypad2 = 0x54,
kVK_ANSI_Keypad3 = 0x55,
kVK_ANSI_Keypad4 = 0x56,
kVK_ANSI_Keypad5 = 0x57,
kVK_ANSI_Keypad6 = 0x58,
kVK_ANSI_Keypad7 = 0x59,
kVK_F20 = 0x5A,
kVK_ANSI_Keypad8 = 0x5B,
kVK_ANSI_Keypad9 = 0x5C,
kVK_JIS_Yen = 0x5D,
kVK_JIS_Underscore = 0x5E,
kVK_JIS_KeypadComma = 0x5F,
kVK_F5 = 0x60,
kVK_F6 = 0x61,
kVK_F7 = 0x62,
kVK_F3 = 0x63,
kVK_F8 = 0x64,
kVK_F9 = 0x65,
kVK_JIS_Eisu = 0x66,
kVK_F11 = 0x67,
kVK_JIS_Kana = 0x68,
kVK_F13 = 0x69,
kVK_F16 = 0x6A,
kVK_F14 = 0x6B,
kVK_F10 = 0x6D,
kVK_F12 = 0x6F,
kVK_F15 = 0x71,
kVK_Help = 0x72,
kVK_Home = 0x73,
kVK_PageUp = 0x74,
kVK_ForwardDelete = 0x75,
kVK_F4 = 0x76,
kVK_End = 0x77,
kVK_F2 = 0x78,
kVK_PageDown = 0x79,
kVK_F1 = 0x7A,
kVK_LeftArrow = 0x7B,
kVK_RightArrow = 0x7C,
kVK_DownArrow = 0x7D,
kVK_UpArrow = 0x7E,
};
/* Indexed by Mac virtual keycode values defined above. */
static const struct {
WORD vkey;
WORD scan;
BOOL fixed;
} default_map[128] = {
{ 'A', 0x1E, FALSE }, /* kVK_ANSI_A */
{ 'S', 0x1F, FALSE }, /* kVK_ANSI_S */
{ 'D', 0x20, FALSE }, /* kVK_ANSI_D */
{ 'F', 0x21, FALSE }, /* kVK_ANSI_F */
{ 'H', 0x23, FALSE }, /* kVK_ANSI_H */
{ 'G', 0x22, FALSE }, /* kVK_ANSI_G */
{ 'Z', 0x2C, FALSE }, /* kVK_ANSI_Z */
{ 'X', 0x2D, FALSE }, /* kVK_ANSI_X */
{ 'C', 0x2E, FALSE }, /* kVK_ANSI_C */
{ 'V', 0x2F, FALSE }, /* kVK_ANSI_V */
{ VK_OEM_102, 0x56, TRUE }, /* kVK_ISO_Section */
{ 'B', 0x30, FALSE }, /* kVK_ANSI_B */
{ 'Q', 0x10, FALSE }, /* kVK_ANSI_Q */
{ 'W', 0x11, FALSE }, /* kVK_ANSI_W */
{ 'E', 0x12, FALSE }, /* kVK_ANSI_E */
{ 'R', 0x13, FALSE }, /* kVK_ANSI_R */
{ 'Y', 0x15, FALSE }, /* kVK_ANSI_Y */
{ 'T', 0x14, FALSE }, /* kVK_ANSI_T */
{ '1', 0x02, FALSE }, /* kVK_ANSI_1 */
{ '2', 0x03, FALSE }, /* kVK_ANSI_2 */
{ '3', 0x04, FALSE }, /* kVK_ANSI_3 */
{ '4', 0x05, FALSE }, /* kVK_ANSI_4 */
{ '6', 0x07, FALSE }, /* kVK_ANSI_6 */
{ '5', 0x06, FALSE }, /* kVK_ANSI_5 */
{ VK_OEM_PLUS, 0x0D, FALSE }, /* kVK_ANSI_Equal */
{ '9', 0x0A, FALSE }, /* kVK_ANSI_9 */
{ '7', 0x08, FALSE }, /* kVK_ANSI_7 */
{ VK_OEM_MINUS, 0x0C, FALSE }, /* kVK_ANSI_Minus */
{ '8', 0x09, FALSE }, /* kVK_ANSI_8 */
{ '0', 0x0B, FALSE }, /* kVK_ANSI_0 */
{ VK_OEM_6, 0x1B, FALSE }, /* kVK_ANSI_RightBracket */
{ 'O', 0x18, FALSE }, /* kVK_ANSI_O */
{ 'U', 0x16, FALSE }, /* kVK_ANSI_U */
{ VK_OEM_4, 0x1A, FALSE }, /* kVK_ANSI_LeftBracket */
{ 'I', 0x17, FALSE }, /* kVK_ANSI_I */
{ 'P', 0x19, FALSE }, /* kVK_ANSI_P */
{ VK_RETURN, 0x1C, TRUE }, /* kVK_Return */
{ 'L', 0x26, FALSE }, /* kVK_ANSI_L */
{ 'J', 0x24, FALSE }, /* kVK_ANSI_J */
{ VK_OEM_7, 0x28, FALSE }, /* kVK_ANSI_Quote */
{ 'K', 0x25, FALSE }, /* kVK_ANSI_K */
{ VK_OEM_1, 0x27, FALSE }, /* kVK_ANSI_Semicolon */
{ VK_OEM_5, 0x2B, FALSE }, /* kVK_ANSI_Backslash */
{ VK_OEM_COMMA, 0x33, FALSE }, /* kVK_ANSI_Comma */
{ VK_OEM_2, 0x35, FALSE }, /* kVK_ANSI_Slash */
{ 'N', 0x31, FALSE }, /* kVK_ANSI_N */
{ 'M', 0x32, FALSE }, /* kVK_ANSI_M */
{ VK_OEM_PERIOD, 0x34, FALSE }, /* kVK_ANSI_Period */
{ VK_TAB, 0x0F, TRUE }, /* kVK_Tab */
{ VK_SPACE, 0x39, TRUE }, /* kVK_Space */
{ VK_OEM_3, 0x29, FALSE }, /* kVK_ANSI_Grave */
{ VK_BACK, 0x0E, TRUE }, /* kVK_Delete */
{ 0, 0, FALSE }, /* 0x34 unused */
{ VK_ESCAPE, 0x01, TRUE }, /* kVK_Escape */
{ VK_RMENU, 0x38 | 0x100, TRUE }, /* kVK_RightCommand */
{ VK_LMENU, 0x38, TRUE }, /* kVK_Command */
{ VK_LSHIFT, 0x2A, TRUE }, /* kVK_Shift */
{ VK_CAPITAL, 0x3A, TRUE }, /* kVK_CapsLock */
{ 0, 0, FALSE }, /* kVK_Option */
{ VK_LCONTROL, 0x1D, TRUE }, /* kVK_Control */
{ VK_RSHIFT, 0x36, TRUE }, /* kVK_RightShift */
{ 0, 0, FALSE }, /* kVK_RightOption */
{ VK_RCONTROL, 0x1D | 0x100, TRUE }, /* kVK_RightControl */
{ 0, 0, FALSE }, /* kVK_Function */
{ VK_F17, 0x68, TRUE }, /* kVK_F17 */
{ VK_DECIMAL, 0x53, TRUE }, /* kVK_ANSI_KeypadDecimal */
{ 0, 0, FALSE }, /* 0x42 unused */
{ VK_MULTIPLY, 0x37, TRUE }, /* kVK_ANSI_KeypadMultiply */
{ 0, 0, FALSE }, /* 0x44 unused */
{ VK_ADD, 0x4E, TRUE }, /* kVK_ANSI_KeypadPlus */
{ 0, 0, FALSE }, /* 0x46 unused */
{ VK_OEM_CLEAR, 0x59, TRUE }, /* kVK_ANSI_KeypadClear */
{ VK_VOLUME_UP, 0 | 0x100, TRUE }, /* kVK_VolumeUp */
{ VK_VOLUME_DOWN, 0 | 0x100, TRUE }, /* kVK_VolumeDown */
{ VK_VOLUME_MUTE, 0 | 0x100, TRUE }, /* kVK_Mute */
{ VK_DIVIDE, 0x35 | 0x100, TRUE }, /* kVK_ANSI_KeypadDivide */
{ VK_RETURN, 0x1C | 0x100, TRUE }, /* kVK_ANSI_KeypadEnter */
{ 0, 0, FALSE }, /* 0x4D unused */
{ VK_SUBTRACT, 0x4A, TRUE }, /* kVK_ANSI_KeypadMinus */
{ VK_F18, 0x69, TRUE }, /* kVK_F18 */
{ VK_F19, 0x6A, TRUE }, /* kVK_F19 */
{ VK_OEM_NEC_EQUAL, 0x0D | 0x100, TRUE }, /* kVK_ANSI_KeypadEquals */
{ VK_NUMPAD0, 0x52, TRUE }, /* kVK_ANSI_Keypad0 */
{ VK_NUMPAD1, 0x4F, TRUE }, /* kVK_ANSI_Keypad1 */
{ VK_NUMPAD2, 0x50, TRUE }, /* kVK_ANSI_Keypad2 */
{ VK_NUMPAD3, 0x51, TRUE }, /* kVK_ANSI_Keypad3 */
{ VK_NUMPAD4, 0x4B, TRUE }, /* kVK_ANSI_Keypad4 */
{ VK_NUMPAD5, 0x4C, TRUE }, /* kVK_ANSI_Keypad5 */
{ VK_NUMPAD6, 0x4D, TRUE }, /* kVK_ANSI_Keypad6 */
{ VK_NUMPAD7, 0x47, TRUE }, /* kVK_ANSI_Keypad7 */
{ VK_F20, 0x6B, TRUE }, /* kVK_F20 */
{ VK_NUMPAD8, 0x48, TRUE }, /* kVK_ANSI_Keypad8 */
{ VK_NUMPAD9, 0x49, TRUE }, /* kVK_ANSI_Keypad9 */
{ 0xFF, 0x7D, TRUE }, /* kVK_JIS_Yen */
{ 0xC1, 0x73, TRUE }, /* kVK_JIS_Underscore */
{ VK_SEPARATOR, 0x7E, TRUE }, /* kVK_JIS_KeypadComma */
{ VK_F5, 0x3F, TRUE }, /* kVK_F5 */
{ VK_F6, 0x40, TRUE }, /* kVK_F6 */
{ VK_F7, 0x41, TRUE }, /* kVK_F7 */
{ VK_F3, 0x3D, TRUE }, /* kVK_F3 */
{ VK_F8, 0x42, TRUE }, /* kVK_F8 */
{ VK_F9, 0x43, TRUE }, /* kVK_F9 */
{ 0xFF, 0x72, TRUE }, /* kVK_JIS_Eisu */
{ VK_F11, 0x57, TRUE }, /* kVK_F11 */
{ VK_OEM_RESET, 0x71, TRUE }, /* kVK_JIS_Kana */
{ VK_F13, 0x64, TRUE }, /* kVK_F13 */
{ VK_F16, 0x67, TRUE }, /* kVK_F16 */
{ VK_F14, 0x65, TRUE }, /* kVK_F14 */
{ 0, 0, FALSE }, /* 0x6C unused */
{ VK_F10, 0x44, TRUE }, /* kVK_F10 */
{ 0, 0, FALSE }, /* 0x6E unused */
{ VK_F12, 0x58, TRUE }, /* kVK_F12 */
{ 0, 0, FALSE }, /* 0x70 unused */
{ VK_F15, 0x66, TRUE }, /* kVK_F15 */
{ VK_INSERT, 0x52 | 0x100, TRUE }, /* kVK_Help */ /* map to Insert */
{ VK_HOME, 0x47 | 0x100, TRUE }, /* kVK_Home */
{ VK_PRIOR, 0x49 | 0x100, TRUE }, /* kVK_PageUp */
{ VK_DELETE, 0x53 | 0x100, TRUE }, /* kVK_ForwardDelete */
{ VK_F4, 0x3E, TRUE }, /* kVK_F4 */
{ VK_END, 0x4F | 0x100, TRUE }, /* kVK_End */
{ VK_F2, 0x3C, TRUE }, /* kVK_F2 */
{ VK_NEXT, 0x51 | 0x100, TRUE }, /* kVK_PageDown */
{ VK_F1, 0x3B, TRUE }, /* kVK_F1 */
{ VK_LEFT, 0x4B | 0x100, TRUE }, /* kVK_LeftArrow */
{ VK_RIGHT, 0x4D | 0x100, TRUE }, /* kVK_RightArrow */
{ VK_DOWN, 0x50 | 0x100, TRUE }, /* kVK_DownArrow */
{ VK_UP, 0x48 | 0x100, TRUE }, /* kVK_UpArrow */
};
static BOOL char_matches_string(WCHAR wchar, UniChar *string, BOOL ignore_diacritics)
{
BOOL ret;
CFStringRef s1 = CFStringCreateWithCharactersNoCopy(NULL, (UniChar*)&wchar, 1, kCFAllocatorNull);
CFStringRef s2 = CFStringCreateWithCharactersNoCopy(NULL, string, strlenW(string), kCFAllocatorNull);
CFStringCompareFlags flags = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareWidthInsensitive;
if (ignore_diacritics)
flags |= kCFCompareDiacriticInsensitive;
ret = (CFStringCompare(s1, s2, flags) == kCFCompareEqualTo);
CFRelease(s1);
CFRelease(s2);
return ret;
}
/***********************************************************************
* macdrv_compute_keyboard_layout
*/
void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
{
int keyc;
WCHAR vkey;
const UCKeyboardLayout *uchr;
const UInt32 modifier_combos[] = {
0,
shiftKey >> 8,
cmdKey >> 8,
(shiftKey | cmdKey) >> 8,
optionKey >> 8,
(shiftKey | optionKey) >> 8,
};
UniChar map[128][sizeof(modifier_combos) / sizeof(modifier_combos[0])][4 + 1];
int combo;
BYTE vkey_used[256];
int ignore_diacritics;
static const struct {
WCHAR wchar;
DWORD vkey;
} symbol_vkeys[] = {
{ '-', VK_OEM_MINUS },
{ '+', VK_OEM_PLUS },
{ '_', VK_OEM_MINUS },
{ ',', VK_OEM_COMMA },
{ '.', VK_OEM_PERIOD },
{ '=', VK_OEM_PLUS },
{ '>', VK_OEM_PERIOD },
{ '<', VK_OEM_COMMA },
{ '|', VK_OEM_5 },
{ '\\', VK_OEM_5 },
{ '`', VK_OEM_3 },
{ '[', VK_OEM_4 },
{ '~', VK_OEM_3 },
{ '?', VK_OEM_2 },
{ ']', VK_OEM_6 },
{ '/', VK_OEM_2 },
{ ':', VK_OEM_1 },
{ '}', VK_OEM_6 },
{ '{', VK_OEM_4 },
{ ';', VK_OEM_1 },
{ '\'', VK_OEM_7 },
{ ':', VK_OEM_PERIOD },
{ ';', VK_OEM_COMMA },
{ '"', VK_OEM_7 },
{ 0x00B4, VK_OEM_4 }, /* 0x00B4 is ACUTE ACCENT */
{ '\'', VK_OEM_2 },
{ 0x00A7, VK_OEM_5 }, /* 0x00A7 is SECTION SIGN */
{ '*', VK_OEM_PLUS },
{ 0x00B4, VK_OEM_7 },
{ '`', VK_OEM_4 },
{ '[', VK_OEM_6 },
{ '/', VK_OEM_5 },
{ '^', VK_OEM_6 },
{ '*', VK_OEM_2 },
{ '{', VK_OEM_6 },
{ '~', VK_OEM_1 },
{ '?', VK_OEM_PLUS },
{ '?', VK_OEM_4 },
{ 0x00B4, VK_OEM_3 },
{ '?', VK_OEM_COMMA },
{ '~', VK_OEM_PLUS },
{ ']', VK_OEM_4 },
{ '\'', VK_OEM_3 },
{ 0x00A7, VK_OEM_7 },
};
int i;
/* Vkeys that are suitable for assigning to arbitrary keys, organized in
contiguous ranges. */
static const struct {
WORD first, last;
} vkey_ranges[] = {
{ 'A', 'Z' },
{ '0', '9' },
{ VK_OEM_1, VK_OEM_3 },
{ VK_OEM_4, VK_ICO_CLEAR },
{ 0xe9, 0xf5 },
{ VK_OEM_NEC_EQUAL, VK_OEM_NEC_EQUAL },
{ VK_F1, VK_F24 },
{ 0, 0 }
};
int vkey_range;
if (!thread_data->keyboard_layout_uchr)
{
ERR("no keyboard layout UCHR data\n");
return;
}
memset(thread_data->keyc2vkey, 0, sizeof(thread_data->keyc2vkey));
memset(vkey_used, 0, sizeof(vkey_used));
for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
{
thread_data->keyc2scan[keyc] = default_map[keyc].scan;
if (default_map[keyc].fixed)
{
vkey = default_map[keyc].vkey;
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (fixed)\n", keyc, vkey);
}
}
if (thread_data->iso_keyboard)
{
/* In almost all cases, the Mac key codes indicate a physical key position
and this corresponds nicely to Win32 scan codes. However, the Mac key
codes differ in one case between ANSI and ISO keyboards. For ANSI
keyboards, the key to the left of the digits and above the Tab key
produces key code kVK_ANSI_Grave. For ISO keyboards, the key in that
some position produces kVK_ISO_Section. The additional key on ISO
keyboards, the one to the right of the left Shift key, produces
kVK_ANSI_Grave, which is just weird.
Since we want the key in that upper left corner to always produce the
same scan code (0x29), we need to swap the scan codes of those two
Mac key codes for ISO keyboards. */
DWORD temp = thread_data->keyc2scan[kVK_ANSI_Grave];
thread_data->keyc2scan[kVK_ANSI_Grave] = thread_data->keyc2scan[kVK_ISO_Section];
thread_data->keyc2scan[kVK_ISO_Section] = temp;
}
uchr = (const UCKeyboardLayout*)CFDataGetBytePtr(thread_data->keyboard_layout_uchr);
/* Using the keyboard layout, build a map of key code + modifiers -> characters. */
memset(map, 0, sizeof(map));
for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
{
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc]) continue; /* assigned a fixed vkey */
TRACE("keyc 0x%04x: ", keyc);
for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
{
UInt32 deadKeyState;
UniCharCount len;
OSStatus status;
deadKeyState = 0;
status = UCKeyTranslate(uchr, keyc, kUCKeyActionDown, modifier_combos[combo],
thread_data->keyboard_type, kUCKeyTranslateNoDeadKeysMask,
&deadKeyState, sizeof(map[keyc][combo])/sizeof(map[keyc][combo][0]) - 1,
&len, map[keyc][combo]);
if (status != noErr)
map[keyc][combo][0] = 0;
TRACE("%s%s", (combo ? ", " : ""), debugstr_w(map[keyc][combo]));
}
TRACE("\n");
}
/* First try to match key codes to the vkeys for the letters A through Z.
Try unmodified first, then with various modifier combinations in succession.
On the first pass, try to get a match lacking diacritical marks. On the
second pass, accept matches with diacritical marks. */
for (ignore_diacritics = 0; ignore_diacritics <= 1; ignore_diacritics++)
{
for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
{
for (vkey = 'A'; vkey <= 'Z'; vkey++)
{
if (vkey_used[vkey])
continue;
for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
{
if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
continue;
if (char_matches_string(vkey, map[keyc][combo], ignore_diacritics))
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
break;
}
}
}
}
}
/* Next try to match key codes to the vkeys for the digits 0 through 9. */
for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
{
for (vkey = '0'; vkey <= '9'; vkey++)
{
if (vkey_used[vkey])
continue;
for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
{
if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
continue;
if (char_matches_string(vkey, map[keyc][combo], FALSE))
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
debugstr_wn(&vkey, 1), debugstr_w(map[keyc][combo]));
break;
}
}
}
}
/* Now try to match key codes for certain common punctuation characters to
the most common OEM vkeys (e.g. '.' to VK_OEM_PERIOD). */
for (i = 0; i < sizeof(symbol_vkeys) / sizeof(symbol_vkeys[0]); i++)
{
vkey = symbol_vkeys[i].vkey;
if (vkey_used[vkey])
continue;
for (combo = 0; combo < sizeof(modifier_combos) / sizeof(modifier_combos[0]); combo++)
{
for (keyc = 0; keyc < sizeof(map) / sizeof(map[0]); keyc++)
{
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc] || !map[keyc][combo][0])
continue;
if (char_matches_string(symbol_vkeys[i].wchar, map[keyc][combo], FALSE))
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (%s match %s)\n", keyc, vkey,
debugstr_wn(&symbol_vkeys[i].wchar, 1), debugstr_w(map[keyc][combo]));
break;
}
}
if (vkey_used[vkey])
break;
}
}
/* For those key codes still without a vkey, try to use the default vkey
from the default map, if it's still available. */
for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
{
DWORD vkey = default_map[keyc].vkey;
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
if (!vkey_used[vkey])
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (default map)\n", keyc, vkey);
}
}
/* For any unassigned key codes which would map to a letter in the default
map, but whose normal letter vkey wasn't available, try to find a
different letter. */
vkey = 'A';
for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
{
if (default_map[keyc].vkey < 'A' || 'Z' < default_map[keyc].vkey)
continue; /* not a letter in ANSI layout */
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
while (vkey <= 'Z' && vkey_used[vkey]) vkey++;
if (vkey <= 'Z')
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (spare letter)\n", keyc, vkey);
}
else
break; /* no more unused letter vkeys, so stop trying */
}
/* Same thing but with the digits. */
vkey = '0';
for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
{
if (default_map[keyc].vkey < '0' || '9' < default_map[keyc].vkey)
continue; /* not a digit in ANSI layout */
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
while (vkey <= '9' && vkey_used[vkey]) vkey++;
if (vkey <= '9')
{
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (spare digit)\n", keyc, vkey);
}
else
break; /* no more unused digit vkeys, so stop trying */
}
/* Last chance. Assign any available vkey. */
vkey_range = 0;
vkey = vkey_ranges[vkey_range].first;
for (keyc = 0; keyc < sizeof(default_map) / sizeof(default_map[0]); keyc++)
{
if (!thread_data->keyc2scan[keyc]) continue; /* not a known Mac key code */
if (thread_data->keyc2vkey[keyc]) continue; /* already assigned */
while (vkey && vkey_used[vkey])
{
if (vkey == vkey_ranges[vkey_range].last)
{
vkey_range++;
vkey = vkey_ranges[vkey_range].first;
}
else
vkey++;
}
if (!vkey)
{
WARN("No more vkeys available!\n");
break;
}
thread_data->keyc2vkey[keyc] = vkey;
vkey_used[vkey] = 1;
TRACE("keyc 0x%04x -> vkey 0x%04x (spare vkey)\n", keyc, vkey);
}
}

View File

@ -84,6 +84,11 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
{
macdrv_event_queue queue;
const macdrv_event *current_event;
CFDataRef keyboard_layout_uchr;
CGEventSourceKeyboardType keyboard_type;
int iso_keyboard;
WORD keyc2vkey[128];
WORD keyc2scan[128];
};
extern DWORD thread_data_tls_index DECLSPEC_HIDDEN;
@ -128,4 +133,6 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
extern void macdrv_mouse_button(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data) DECLSPEC_HIDDEN;
#endif /* __WINE_MACDRV_H */

View File

@ -219,4 +219,8 @@ extern void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat
extern void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha) DECLSPEC_HIDDEN;
extern void macdrv_give_cocoa_window_focus(macdrv_window w) DECLSPEC_HIDDEN;
/* keyboard */
extern CFDataRef macdrv_copy_keyboard_layout(CGEventSourceKeyboardType* keyboard_type, int* is_iso) DECLSPEC_HIDDEN;
#endif /* __WINE_MACDRV_COCOA_H */

View File

@ -60,6 +60,8 @@ static void thread_detach(void)
if (data)
{
macdrv_destroy_event_queue(data->queue);
if (data->keyboard_layout_uchr)
CFRelease(data->keyboard_layout_uchr);
HeapFree(GetProcessHeap(), 0, data);
}
}
@ -116,6 +118,9 @@ struct macdrv_thread_data *macdrv_init_thread_data(void)
ExitProcess(1);
}
data->keyboard_layout_uchr = macdrv_copy_keyboard_layout(&data->keyboard_type, &data->iso_keyboard);
macdrv_compute_keyboard_layout(data);
set_queue_display_fd(macdrv_get_event_queue_fd(data->queue));
TlsSetValue(thread_data_tls_index, data);